Exception Filters

Nest ships an exception hierarchy that maps to status codes, so a service says what went wrong and never touches a response object: NotFoundException is 404, ConflictException 409, ForbiddenException 403, UnauthorizedException 401, and HttpException(body, status) covers the rest. Anything else that escapes becomes 500 with no details leaked. The default payload is { "statusCode": 409, "message": "...", "error": "Conflict" }; Bookshelf publishes the envelope of The Bookshelf API, so one filter reshapes it. @Catch(HttpException) narrows a filter, and narrower filters registered closer to a handler win.

src/common/http-exception.filter.ts — one payload for every failureJavaScript
@Catch()                                    // no argument: catch everything
export class ProblemFilter implements ExceptionFilter {
  private readonly logger = new Logger('ProblemFilter');  // APP_FILTER keeps it injectable
  catch(exception: unknown, host: ArgumentsHost) {
    const [res, req] = [host.switchToHttp().getResponse(), host.switchToHttp().getRequest()];
    const http = exception instanceof HttpException;
    const status = http ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
    const body = (http ? exception.getResponse() : {}) as Record<string, any>;
    const many = Array.isArray(body.message);   // ValidationPipe throws an array of them
    if (status >= 500) this.logger.error(String(exception));   // log it, send none of it
    res.status(status).json({ requestId: req.requestId,
      error: { code: body.code ?? CODES[status] ?? 'error',
               message: many ? 'Request validation failed' : body.message,
               ...(many ? { details: body.message } : {}) } }); } }

That array test is how validation failures are recognized: ValidationPipe sets message to an array of strings, every other built-in exception uses one string. CODES maps status to the stable machine-readable code of Problem Details — clients branch on error.code, never on the sentence.

Output of 109
$ curl -s localhost:4310/api/v1/books/bk_99                                           # 404
{"error":{"code":"not_found","message":"No book with id bk_99"},"requestId":"b229ac72"}
$ curl -s -X POST .../books/bk_3/reviews -d '{"rating":4,...}'   # a second review: 409
{"error":{"code":"conflict","message":"You have already reviewed this book"},..}

An exception can also carry its own code by taking an object instead of a string, as login does: new UnauthorizedException({ code: 'bad_credentials', message: '...' }).