Appearance
Global Error Handler
This library provides a way to handle fetch errors automatically. Errors will also be tracked in Application Insights when provided.
WARNING
There is no out-of-the-box support for Axios yet. Please contact the Codey team for more information.
Dependencies
- Vue: 3.x.x
- Pinia: 2.x.x
- Vue Use: 9.x.x
- Codey Core Localized Routing
Usage
Installation
To have errors handled, global error handling needs to be set up using setupGlobalErrorHandler
, providing the localizedRouter
and optional options (so if you don't want a certain error to be handled, it can be omitted from the options).
The available options are in line with how we as Codey want to see those errors handled from a UX standpoint:
The not found
, forbidden
, and unauthorized
errors will be redirected to their corresponding pages.
The internal server error
is provided as a callback, which allows for more flexibility in how this is handled. We found that this can differ from app to app.
For a back-office application, we recommend using a toast to show that something went wrong. For a public-facing application, it might be better to show a more generic error page.
Make sure to use the localizedRouting
instance from the @xerius/codey-core
package to navigate to the error pages. For more information on how to use this, please refer to the localized routing documentation.
INFO
The registration can also be done in the main.ts
file; just make sure it's done after the router initialization.
Be aware that this will prevent usage of composables like useToast()
, as they need to be used within a Vue setup
method.
vue
<script setup lang="ts">
import { useToast } from "primevue/usetoast";
import { useApplicationInsights } from "@xerius/codey-core/modules/application-insights";
import { setupGlobalErrorHandler } from "@xerius/codey-core/modules/global-error-handler";
const toast = useToast();
setupGlobalErrorHandler(
localizedRouting,
{
notFoundRoute: { name: "NotFound" },
forbiddenRoute: { name: "Forbidden" },
unauthorizedRoute: { name: "Unauthorized" },
internalServerErrorAction: () => {
toast.add({
severity: "error",
summary: "Internal Server Error",
detail: "Oeps, er ging iets mis. Probeer later nog eens ...",
life: 3000,
});
},
},
useApplicationInsights
);
</script>
ts
import { createI18n } from "vue-i18n";
import { registerI18nMessageLoader } from "@xerius/codey-core/modules/i18n";
import { useApplicationInsights } from "@xerius/codey-core/modules/application-insights";
import { registerI18nMessageLoader, postTranslationHandler } from "@xerius/codey-core/modules/i18n";
import { createLocalizedRouting } from "@xerius/codey-core/modules/localized-routing";
import { setupGlobalErrorHandler } from "@xerius/codey-core/modules/global-error-handler";
import { fetchTranslations } from "./api/translations.api";
import { localizedAppNames } from "./router/routes";
import App from "./App.vue";
import router from "@/router";
const i18n = createI18n({
legacy: false,
locale: "nl-be",
fallbackLocale: "nl-be",
globalInjection: true,
postTranslation: postTranslationHandler,
});
registerI18nMessageLoader(i18n, fetchTranslations);
const localizedRouting = createLocalizedRouting(router, i18n, localizedAppNames);
setupGlobalErrorHandler(
localizedRouting,
{
notFoundRoute: { name: "NotFound" },
forbiddenRoute: { name: "Forbidden" },
unauthorizedRoute: { name: "Unauthorized" },
internalServerErrorAction() {
localizedRouting.pushLocalizedRoute("Oeps");
},
},
useApplicationInsights
);
const app = createApp(App);
// ...
ApplicationInsights
You have the option of providing your Application Insights instance to the setupGlobalErrorHandler()
function.
WARNING
Currently, the instance will only be used to call trackTrace()
or trackException()
in handleFetchError()
.
Other error handling will not make use of the provided Application Insights instance.
handleFetchError
To handle network failures automatically, you need to provide an error handler to the fetch
calls. This can be easily done using the useFetch composable from the @vueuse/core
library and providing it the handleFetchError
as an error handler.
ts
import { useFetch } from "@vueuse/core";
import { useGlobalErrorHandler } from "@xerius/codey-core/modules/global-error-handler";
const globalErrorHandler = useGlobalErrorHandler();
const { data } = useFetch(url, {
onFetchError(ctx) {
return globalErrorHandler.handleFetchError(ctx);
},
});
For now, we focus on the fetch
API with useFetch
from VueUse. Support for Axios
can be provided upon request. If errors need to be handled in a specific way, you can omit this function and handle them manually.
useGlobalErrorStore
Along with the error handling, we store error data within the globalError
store. This can be accessed to get extra information or perform specific error handling yourself when needed.
handleGlobalError
This is the error handling function used internally by handleFetchError
and can be used to trigger error handling yourself.
This function is available on the globalErrorHandler
instance retrieved by useGlobalErrorHandler
.
Reset error state
If the error state is used to show or hide parts of your application, it will be necessary to reset this once a page navigation has occurred.
This can be easily done within the router configuration with the following:
ts
import { useGlobalErrorStore } from "@xerius/codey-core/modules/global-error-handler";
router.beforeEach(() => {
useGlobalErrorStore().$reset();
});
Custom State Layer
If your application does not depend on Pinia
and you don't want to introduce this dependency purely for the global error handler, you can create your own custom state layer using, for example, a composable
.
Pass your implementation that meets the GlobalErrorHandlerStore
interface to the setupGlobalErrorHandler
function.
Note
Creating your own store also means carrying the responsibility of implementing the necessary state management logic and functions like reset
.
ts
import { reactive } from "vue";
import type { GlobalErrorState } from "@xerius/codey-core/global-error-handler";
const state = reactive<GlobalErrorState>({
errorType: undefined,
error: undefined,
});
export const useGlobalErrorState = () => {
return state;
};
ts
import { createI18n } from "vue-i18n";
import { registerI18nMessageLoader } from "@xerius/codey-core/modules/i18n";
import { useApplicationInsights } from "@xerius/codey-core/modules/application-insights";
import { registerI18nMessageLoader, postTranslationHandler } from "@xerius/codey-core/modules/i18n";
import { createLocalizedRouting } from "@xerius/codey-core/modules/localized-routing";
import { setupGlobalErrorHandler } from "@xerius/codey-core/modules/global-error-handler";
import { useGlobalErrorState } from "@/use/useGlobalErrorState";
import { fetchTranslations } from "./api/translations.api";
import { localizedAppNames } from "./router/routes";
import App from "./App.vue";
import router from "@/router";
const i18n = createI18n({
legacy: false,
locale: "nl-be",
fallbackLocale: "nl-be",
globalInjection: true,
postTranslation: postTranslationHandler,
});
registerI18nMessageLoader(i18n, fetchTranslations);
const localizedRouting = createLocalizedRouting(router, i18n, localizedAppNames);
setupGlobalErrorHandler(
localizedRouting,
{
notFoundRoute: { name: "NotFound" },
forbiddenRoute: { name: "Forbidden" },
unauthorizedRoute: { name: "Unauthorized" },
internalServerErrorAction() {
localizedRouting.pushLocalizedRoute("Oeps");
},
},
useApplicationInsights,
useGlobalErrorState
);
const app = createApp(App);
// ...