ErrorParser
import: @azure-net/kit
The main task of the error parser is to bring all errors in the @azure-net/kit application to a unified format. It is also used internally by createAsyncAction and createAsyncResource, when creating them you can customize the parser and pass your custom parser to createAsyncAction and createAsyncResource.
Globally, at the application level, three types of errors are created:
- HttpServiceError
- SchemaFail
- Error (basic JS error)
All these errors, passing through the parser, come to a unified AppError format
typescript
export type AppErrorType = 'http' | 'app' | 'schema';
// http means the error occurred at the request level, most likely in the repository, since HttpServiceError is created there.
// app means that somewhere specifically you threw an exception through throw Error(), although catching it in the application is real, but difficult
// schema means that the error appeared at the validation level in the schema, which means validation simply failed
export interface AppError<T = unknown, CustomErrorField = never> {
type: AppErrorType; // error type
message: string; // message
fields?: RequestErrors<T>; // validation fields if the error appeared in the schema, or you customized and added them
status?: number; // status if it's an http error, for schema 422, for app no status if you didn't add it
original?: HttpServiceError<T> | SchemaFail<T> | Error; // Original error
custom?: CustomErrorField; // Custom field, initially absent, but you can add your own data there.
}Example of Creating a Parser
typescript
import { createErrorParser, createAsyncHelpers } from '@azure-net/kit';
import { createPresenterFactory } from '@azure-net/kit/edges';
export const ErrorParser = createErrorParser();
// Globally, it is assumed that you embed this parser in AsyncActions and your basic presenter.
// After this, all your requests with errors will be brought to a unified format, and you will also be able to parse errors in the presenter in a unified way
export const AsyncHelpers = createAsyncHelpers({ parseError: ErrorParser });
export const AppPresenter = createPresenterFactory({ ...AsyncHelpers, parseError: ErrorParser });Example of Parser Customization
The parser accepts an object with three keys:
- parseBaseError
- parseHttpError
- parseSchemaError
All these methods must return AppError for the parser to work with them.
typescript
import { createPresenterFactory } from '@azure-net/kit/edges';
import { EnvironmentUtil } from '@azure-net/kit/tools';
import { notificationManager } from '$widgets';
import { createAsyncHelpers, createErrorParser, type AppError, type SchemaFail, type RequestErrors } from '@azure-net/kit';
import { type HttpServiceError } from '@azure-net/kit/infra';
// parseBaseError - customization example
export const parseBaseError = <D = never>(error: Error): AppError<never, D> => {
if (EnvironmentUtil.isBrowser) {
const { alert } = notificationManager();
alert('shared.somethingWentWrong');
}
return {
type: 'app',
message: error.message,
original: error
};
};
// parseSchemaError - customization example, you are free to change keys or add custom ones
export const parseSchemaError = <SchemaData = unknown, D = never>(error: SchemaFail<SchemaData>): AppError<SchemaData, D> => {
return {
type: 'schema',
message: 'schema validation error',
status: 422,
fields: error.getErrors(),
original: error
};
};
// parseHttpError - customization example considering that validation also comes from the backend, which we cannot handle for example
// The backend developer as usual sends validation as an array and we configure the parser once and no longer print its errors 100 times
const parseHttpError = <T = unknown, D = never>(error: HttpServiceError<T>): AppError<T, D> => {
const notFatalErrors = [400, 422, 401];
if (!notFatalErrors.includes(error.status) && EnvironmentUtil.isBrowser) {
const { alert } = notificationManager();
alert('shared.somethingWentWrong');
}
const parseValidationErrors = (): RequestErrors<T> => {
const fields: RequestErrors<T> = {};
const entries: [string, string[]][] = Object.entries((error?.data as { data: Record<keyof T, string[]> })?.data ?? {});
entries.forEach(([key, value]) => {
fields[key as keyof typeof fields] = (Array.isArray(value) ? value[0] : value) as (typeof fields)[keyof typeof fields];
});
return fields;
};
return {
type: 'http',
message: error.message ?? 'unexpected error',
status: error.status ?? 500,
original: error,
fields: notFatalErrors.includes(error.status) ? parseValidationErrors() : undefined
};
};
// Then feed everything into createErrorParser, AsyncHelpers and Presenter
export const ErrorHandler = createErrorParser({ parseBaseError, parseHttpError, parseSchemaError });
export const AsyncHelpers = createAsyncHelpers({ parseError: ErrorHandler });
export const AppPresenter = createPresenterFactory({ ...AsyncHelpers, handleError: ErrorHandler });