typescript
58 lines · 8 steps
A catch-all exception filter in NestJS
One filter turns any thrown error into a consistent, structured JSON response.
Explained by
highlit
1import {
2 ArgumentsHost,
3 Catch,
4 ExceptionFilter,
5 HttpException,
6 HttpStatus,
7 Logger,
8} from '@nestjs/common';
9import { HttpAdapterHost } from '@nestjs/core';
10import { Request } from 'express';
11
12interface ErrorResponse {
13 statusCode: number;
14 message: string | string[];
15 error: string;
16 path: string;
17 timestamp: string;
18}
19
20@Catch()
21export class AllExceptionsFilter implements ExceptionFilter {
22 private readonly logger = new Logger(AllExceptionsFilter.name);
23
24 constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
25
26 catch(exception: unknown, host: ArgumentsHost): void {
27 const { httpAdapter } = this.httpAdapterHost;
28 const ctx = host.switchToHttp();
29 const request = ctx.getRequest<Request>();
30
31 const isHttp = exception instanceof HttpException;
32 const statusCode = isHttp
33 ? exception.getStatus()
34 : HttpStatus.INTERNAL_SERVER_ERROR;
35
36 const cause = isHttp ? exception.getResponse() : null;
37 const message =
38 typeof cause === 'object' && cause !== null && 'message' in cause
39 ? (cause as { message: string | string[] }).message
40 : isHttp
41 ? exception.message
42 : 'Internal server error';
43
44 const body: ErrorResponse = {
45 statusCode,
46 message,
47 error: HttpStatus[statusCode] ?? 'Error',
48 path: request.url,
49 timestamp: new Date().toISOString(),
50 };
51
52 if (statusCode >= HttpStatus.INTERNAL_SERVER_ERROR) {
53 this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : exception);
54 }
55
56 httpAdapter.reply(ctx.getResponse(), body, statusCode);
57 }
58}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A single @Catch() filter centralizes error formatting so every route returns the same shape.
- 2Using the HttpAdapterHost keeps the filter platform-agnostic instead of tied to Express directly.
- 3Distinguishing HttpException from unknown throws lets you preserve intended status codes while defaulting the rest to 500.
Related explainers
python
from flask import Blueprint, jsonify from sqlalchemy import text from sqlalchemy.exc import SQLAlchemyError
Building a health check endpoint in Flask
health-check
blueprint
error-handling
Intermediate
6 steps
java
@Component public class RegionCacheWarmer implements SmartInitializingSingleton { private static final Logger log = LoggerFactory.getLogger(RegionCacheWarmer.class);
Warming a Spring cache at startup
caching
startup-hook
dependency-injection
Intermediate
7 steps
typescript
import { Controller, Param, Sse, MessageEvent } from '@nestjs/common'; import { Observable, interval, merge } from 'rxjs'; import { filter, map, takeWhile } from 'rxjs/operators'; import { JobService } from './job.service';
Streaming job progress with SSE in NestJS
server-sent-events
reactive-streams
rxjs
Intermediate
8 steps
rust
#[derive(Debug, Clone, PartialEq)] pub enum Token { Number(f64), Plus,
How a tokenizer turns text into tokens
lexing
enums
iterators
Intermediate
8 steps
typescript
import { Controller, All, Req,
Catch-all routes and error shaping in NestJS
exception-handling
routing
middleware
Intermediate
6 steps
python
from datetime import date, timedelta from typing import Annotated from fastapi import APIRouter, Depends, Query
Validating date ranges with FastAPI dependencies
dependency-injection
validation
pydantic
Intermediate
6 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-catch-all-exception-filter-in-nestjs-explained-typescript-45d2/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.