Error Handling

All errors thrown within Routes/Hooks will be caught and added to the internalErrors Array in the CallContext.request object.

When there is an Error in a route/hook the rest of the hooks/routes in the execution path are not executed. Hooks can still get executed if runOnError is set to true.

mion does not include any logger by default so applications should typically include a "log" hook to handle and log errors.

RpcError

mion provides the RpcError class to help with the creation and serialization of errors, response status code etc... It also automatically generates an uuid when RouterOptions.autoGenerateErrorId is enabled. This is useful so the error can be traced between the client and the server.

Routes and Hooks should throw/return RpcErrors specifying status code, error name, and publicMessage in case we want to return a message to the client.

RpcErrors get anonymized when returned to the client, this means stack trace and messages are never sent to the public. To return a message to the client use the publicMessage property when creating the error.

RpcErrors can be serialized and unlike regular Errors in JS, the message gets also serialized so it can be properly logged as JSON.

Please note autoGenerateErrorId is disabled by default.

Throwing RpcError

export const getPet = route(async (ctx, id: string): Promise<Pet | RpcError> => {
    try {
        const pet = await myApp.db.getPet(id);
        if (!pet) {
            // Only statusCode and publicMessage will be returned in the response.body
            const statusCode = StatusCodes.BAD_REQUEST;
            const publicMessage = `Pet with id ${id} can't be found`;
            // either return or throw are allowed
            return new RpcError({statusCode, publicMessage});
        }
        return pet;
    } catch (dbError) {
        const statusCode = StatusCodes.INTERNAL_SERVER_ERROR;
        const publicMessage = `Cant fetch data.`;
        /*
         * Only statusCode and publicMessage will be returned in the response.body.
         *
         * Full RpcError containing dbError message and stacktrace will be added
         * to ctx.request.internalErrors, so it can be logged or managed after
         */
        return new RpcError({statusCode, publicMessage, originalError: dbError as Error});
    }
}) satisfies Route;

Throwing Error

If a generic Error is thrown/returned by any route/hook, then a generic 500, Unknown RpcError is generated by mion and returned to the client.

export const alwaysError = route((): void => {
    throw new Error('will generate a 500 error with an "Unknown Error" message');
}) satisfies Route;

Type Reference

RpcError

export interface RpcErrorParams {
    /** id of the error. */
    id?: number | string;
    /** response status code */
    statusCode: number;
    /** the message that will be returned in the response */
    publicMessage?: string;
    /**
     * the error message, it is private and wont be returned in the response.
     * If not defined, it is assigned from originalError.message or publicMessage.
     */
    message?: string;
    /** options data related to the error, ie validation data */
    errorData?: unknown;
    /** original error used to create the RpcError */
    originalError?: Error;
    /** name of the error, if not defined it is assigned from status code */
    name?: string;
}

AnonymRpcError

export interface AnonymRpcError extends RpcErrorParams {
    // when a RpcError gets anonymized the publicMessage becomes the message.
    message: string;
    statusCode: number;
    name: string;
}