@getanthill/datastore
Version:
Event-Sourced Datastore
72 lines (63 loc) • 2.05 kB
text/typescript
import type {
ErrorRequestHandler,
NextFunction,
Request,
Response,
} from 'express';
import type { ResponseError, Services } from '../../typings';
import statusCodes from 'http-status-codes';
export function errorHandler(services: Services): ErrorRequestHandler {
/**
* Note: the last argument `next` is important because if its not present, the function
* will not be called at all.
*/
return (
err: ResponseError,
_req: Request,
res: Response,
_next: NextFunction,
): void => {
if (typeof err !== 'object') {
// If the object is not an Error, create a representation that appears to be
err = {
name: 'Internal Server Error',
// eslint-disable-line no-param-reassign
status: statusCodes.INTERNAL_SERVER_ERROR,
message: String(err), // Coerce to string
};
} else {
// Ensure that err.message is enumerable (It is not by default)
Object.defineProperty(err, 'message', { enumerable: true });
Object.defineProperty(err, 'status', {
enumerable: true,
value: err.status || statusCodes.INTERNAL_SERVER_ERROR,
});
}
if (err.status === statusCodes.INTERNAL_SERVER_ERROR) {
services.telemetry.logger.error(
'[Express#ErrorHandler] Internal server error',
err,
);
}
const errorBody = {
message: err.message,
status: err.status,
details: err.details ?? [],
};
// If we have a server error, then we need to obfuscate its details in the
// response.
if (err.status === statusCodes.INTERNAL_SERVER_ERROR) {
services.telemetry.logger.error(
'[Express#ErrorHandler] Internal Server Error',
{ err },
);
errorBody.message = 'Internal Server Error';
}
services.telemetry.logger.debug('[Express#ErrorHandler] Error response', {
errorBody,
});
res.locals.meter &&
res.locals.meter({ state: err.status, ...res.locals.attributes });
res.status(err.status!).json(errorBody);
};
}