AppError
Type : class
The AppError
class is designed to manage and handle errors of a specific type in a structured way within an application. It provides mechanisms to collect, report multiple errors, while also supporting error scopes for more granular error management. The class includes methods to push new errors, aggregate errors, and convert them to a string representation. It also provides functionality to catch and throw errors, with custom options for error handling behavior.
The main use case for this class is to create a central place to handle errors in an application, making it easier to manage and report errors in a consistent and structured way.
Let’s take an example without using the AppError
class:
if (!user.email) // handle error;if (!user.password) // handle error;... other validation checks
Now the problem is that we need to do the same thing for all other functions that might throw errors like this example:
try { validateUserData(user); const isUserExist = await isUserExist(user); if (isUserExist) {'do something'}; const user = await createUser(user);} catch (error) { 'handle errors'}
Each of the functions used in the previous example might throw it’s own errors, and if we don’t handle each of them separately we don’t really know which error is the one that actually happened. and it will be difficult to handle if we need to return all related errors together to the client.
Now let’s use the AppError
class:
import { AppError } from '@mustib/utils'
const appError = new AppError();if (!user.email) appError.push('Validation', 'email is required');if (!user.password) appError.push('Validation', 'password is required')... other validation checksappError.end()
AppError.aggregate(async (appError) => { await appError.catch(() => { validateUserData(user); });
const isUserExist = await isUserExist(user);
if (isUserExist) appError.push('Duplication', 'user already exist'); else await appError.catch(async () => { await createUser(user) })})
Now we only need to handle the AppError
instance, and we can return all related errors together to the client like this.
try { ... implementations} catch (error) { if (error instanceof AppError) { return error.toString({ includesScope: ['user', 'Validation'], excludesScope: ['secret scope'], }); } else {'send generic error message'}}
With that we only need to handle errors in a central place.
Definition
export class AppError<ErrorTypes extends Capitalize<string>> extends Error {}
Generics
ErrorTypes
- a capitalized string union of error types that are allowed to be pushed to the error instance- For Example
type ErrorTypes = 'Validation' | 'Duplication';const appError = new AppError<ErrorTypes>();appError.push('Validation', 'email is required');
- For Example
Constructor()
type AppError = new (options?: ErrorOptions): AppError
- parameters:
options?:
ErrorOptions
Properties
length
visibility : protected
Type : number
default: : 0
Indicates the number of error types present in the instance
message
visibility : public
Type : string
- a getter that returns the error message containing all errors
errors
visibility : protected
type errors = {[key in ErrorTypes]?: { message: string; scope?: ErrorScope }[]}
- an object that contains all errors of the instance. The keys of the object are
the error types, and the values are arrays of objects with the following
properties:
message
: the error messagescope?
: the scope of the error
Methods
throw()
visibility : public static
type throw = <ErrorTypes extends Capitalize<string>>( type: ErrorTypes, error: string | string[], options?: ErrorOptions & { pushOptions?: PushOptions }, ): never
This method is used to create a new AppError
instance, push the error to it, and throw it instead of having to it that manually.
parameters:
type:
ErrorTypeserror:
string | string[]- see push error parameter for more details
options?:
ErrorOptions & { pushOptions?: PushOptions }
Usage:
AppError.throw('Error Type', 'Error Message')
aggregate()
visibility : public static
type aggregate = async <ErrorTypes extends Capitalize<string>>( aggregateFunc: ( appError: Omit<AppError<ErrorTypes>, 'end'>, ) => void | Promise<void>, options?: ErrorOptions, ): Promise<void>
This method is used if you don’t want to create a new AppError
instance and end it manually by calling the end()
method at the end of your implementations as it does it for you.
-
parameters:
aggregateFunc:
(appError: Omit<AppError<ErrorTypes>, ‘end’>) => void | Promise<void>- a function that will be called with a new
AppError
instance as a parameter. - it can be async or normal function.
- a function that will be called with a new
options?:
ErrorOptions
-
returns:
Promise<void>
Usage:
AppError.aggregate(async (appError) => { appError.push('Error Type', 'Error Message'); appError.catch(async () => { await doSomething(); })})
catch()
visibility : public
type catch = async (catchFunc: () => void | Promise<void>): Promise<void>
This method is used to catch errors thrown from other AppError
instances and add them to the current AppError
instance.
-
parameters:
catchFunc:
() => void | Promise<void>- it will be called inside a try catch block to catch errors thrown from other
AppError
instances. - it can be async or normal function.
- it will be called inside a try catch block to catch errors thrown from other
-
returns:
Promise<void>
Usage:
const appError = new AppError();await appError.catch(async () => { await doSomething();});
push()
visibility : public
type push = (type: ErrorTypes, error: string | string[], options?: PushOptions): void
This method is used to add new errors to the AppError
instance’s errors property.
-
parameters:
type:
ErrorTypeserror:
string | string[]string
- the error message to be addedstring[]
- an array of error messages to be added
options?:
PushOptions
-
returns:
void
Usage:
const appError = new AppError();appError.push('Error Type', ['Error Message 1', 'Error Message 2'], {scope: ['user']});appError.push('Another Error Type', 'Another Error Message', {scope: ['validation']});
matchesScope()
visibility:
protected
type matchesScope = (options: { errScope: ErrorScope; includesScope?: ErrorScope; excludesScope?: ErrorScope; }): boolean
This method check if the error can be displayed or not based on the includesScope
and excludesScope
provided.
It returns true
if the error scope has any scope in includesScope
and has no scope in excludesScope
or there is no includes or excludes scope and returns false
otherwise.
parameters:
options:
an object with the following properties:errScope: ErrorScope
- the scope of the error to checkincludesScope?: ErrorScope
- an optional array of scopes to includeexcludesScope?: ErrorScope
- an optional array of scopes to exclude
returns:
boolean
toString()
visibility : public
type toString = (options?: Omit< Parameters<AppError<ErrorTypes>['matchesScope']>['0'], 'errScope' >, ): string
This method is used to get a string representation of the AppError
instance.
-
parameters:
options?:
the same as the matchesScope method, but without theerrScope
property as it will be the scope of the current error.
-
returns:
string
Usage:
const appError = new AppError();const allErrors = appError.toString();const validationErrors = appError.toString({includesScope: ['validation']});const otherErrors = appError.toString({excludesScope: ['validation']});
throw()
visibility : protected
type throw = (): never
throws the AppError
instance even if there are no errors.
parameters:
undefinedreturns:
never
end()
visibility : public
type end (): void
throws the AppError
instance if there are errors.
parameters:
undefinedreturns:
void
Usage:
const appError = new AppError();appError.end();
ErrorScope
type ErrorScope = (string | symbol)[] | undefined
The error scope is an array of strings and/or symbols that can be used to filter errors based on their scope.
ErrorOptions
type ErrorOptions = { indentation?: number; stackTraceConstructor?: Func;};
-
indentation
- used to indent the error message for better readability
- default value is:
4
-
stackTraceConstructor
- used as
Error.captureStackTrace(error, stackTraceConstructor)
when throwing an error - default value is:
AppError.prototype.throw
- used as
PushOptions
type PushOptions = { scope?: ErrorScope }
-
scope
- See ErrorScope for more details