diff --git a/src/presentation/http/decorators/domainError.ts b/src/presentation/http/decorators/domainError.ts new file mode 100644 index 00000000..25b39375 --- /dev/null +++ b/src/presentation/http/decorators/domainError.ts @@ -0,0 +1,24 @@ +import { StatusCodes } from 'http-status-codes'; +import type { FastifyReply } from 'fastify'; + +/** + * Custom method for sending 500 error + * + * Send this error when a domain-level error is thrown + * + * @example + * + * if (note.creatorId !== userId) { + * return reply.domainError('Note with id ${id} was not updated'); + * } + * + * @param message - custom message + */ +export default async function domainError(this: FastifyReply, message = 'Domain level error'): Promise { + await this + .code(StatusCodes.INTERNAL_SERVER_ERROR) + .type('application/json') + .send({ + message, + }); +} \ No newline at end of file diff --git a/src/presentation/http/decorators/index.ts b/src/presentation/http/decorators/index.ts index 982f4a9d..3504dfb8 100644 --- a/src/presentation/http/decorators/index.ts +++ b/src/presentation/http/decorators/index.ts @@ -2,10 +2,12 @@ import notFound from './notFound.js'; import forbidden from './forbidden.js'; import unauthorized from './unauthorized.js'; import notAcceptable from './notAcceptable.js'; +import domainError from './domainError.js'; export { notFound, forbidden, unauthorized, - notAcceptable + notAcceptable, + domainError }; diff --git a/src/presentation/http/fastify.d.ts b/src/presentation/http/fastify.d.ts index ec723764..3a8e09e6 100644 --- a/src/presentation/http/fastify.d.ts +++ b/src/presentation/http/fastify.d.ts @@ -125,5 +125,20 @@ declare module 'fastify' { * @param message - Optional message to send. If not specified, default message will be sent */ notAcceptable: (message?: string) => Promise; + + /** + * Custom method for sending 500 error + * + * Send this error when a domain-level error is thrown + * + * @example + * + * if (note.creatorId !== userId) { + * return reply.domainError('Note with id ${id} was not updated'); + * } + * + * @param message - Optional message to send. If not specified, default message will be sent + */ + domainError: (message?: string) => Promise; } } diff --git a/src/presentation/http/http-api.ts b/src/presentation/http/http-api.ts index 6027db9e..5868bf22 100644 --- a/src/presentation/http/http-api.ts +++ b/src/presentation/http/http-api.ts @@ -10,7 +10,7 @@ import fastifySwagger from '@fastify/swagger'; import fastifySwaggerUI from '@fastify/swagger-ui'; import addUserIdResolver from '@presentation/http/middlewares/common/userIdResolver.js'; import cookie from '@fastify/cookie'; -import { notFound, forbidden, unauthorized, notAcceptable } from './decorators/index.js'; +import { notFound, forbidden, unauthorized, notAcceptable, domainError } from './decorators/index.js'; import NoteRouter from '@presentation/http/router/note.js'; import OauthRouter from '@presentation/http/router/oauth.js'; import AuthRouter from '@presentation/http/router/auth.js'; @@ -79,6 +79,8 @@ export default class HttpApi implements Api { this.addPoliciesCheckHook(); await this.addApiRoutes(domainServices); + + this.domainErrorHandler(); } @@ -278,6 +280,7 @@ export default class HttpApi implements Api { this.server?.decorateReply('forbidden', forbidden); this.server?.decorateReply('unauthorized', unauthorized); this.server?.decorateReply('notAcceptable', notAcceptable); + this.server?.decorateReply('domainError', domainError); } /** @@ -320,5 +323,20 @@ export default class HttpApi implements Api { }); }); } + + /** + * Domain error handler + */ + private domainErrorHandler(): void { + this.server?.setErrorHandler(function (error, request, reply) { + const statusCode = error.statusCode; + + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + if (statusCode === 500) { + this.log.error(error); + void reply.domainError(error.message); + } + }); + } } diff --git a/src/presentation/http/router/note.ts b/src/presentation/http/router/note.ts index 97b7b781..3a9f298e 100644 --- a/src/presentation/http/router/note.ts +++ b/src/presentation/http/router/note.ts @@ -253,12 +253,6 @@ const NoteRouter: FastifyPluginCallback = (fastify, opts, don }); }); - fastify.setErrorHandler(function (error, request, reply) { - this.log.error(error); - // eslint-disable-next-line @typescript-eslint/no-magic-numbers - void reply.status(409).send({ ok: false }); - }); - done(); };