🧱 D4 · Exception Filter and HttpException

D4 · Exception Filter and HttpException: Nest's Exception Filter is the **decorator-marked EXACT counterpart** of Spring's `@RestControllerAdvice`: in Spring, `@ExceptionHandler(

Nest's Exception Filter is the **decorator-marked EXACT counterpart** of Spring's `@RestControllerAdvice`: in Spring, `@ExceptionHandler(NotFoundException.class)` tells a method "YOU catch this exception type"; in Nest, `@Catch(HttpException)` tells a class the SAME thing — which error TYPE falls into this filter is EXPLICITLY written in the decorator (unlike Express's C5, where it is the parameter COUNT, not a decorator TYPE, that decides). `HttpException` (and its subclasses like `NotFoundException`, `BadRequestException`) are Nest's own ready-made error classes — writing `throw new NotFoundException('Bug not found')` is equivalent to throwing a custom exception class in Spring. So if Nest's own DEFAULT exception behavior already returns JSON (a step ahead of Express's HTML), why still write a custom filter? Because the SHAPE of the error body a tester expects (`{ error: "..." }` or `{ message: "...", statusCode: ... }`) depends on the project's CONTRACT — the default behavior returns "something" but returning something that MATCHES THE CONTRACT is not GUARANTEED; writing AND registering the filter is what provides that guarantee.

Writing a Custom Filter and Registering It Globally

Micro Lab: Code practice

Replace the TODO line with the critical line from the expected solution. This is not a real runtime; the goal is to reinforce writing the correct structure in a controlled way.

Step by Step: Code practice

Identify goal and input

Complete the critical line

Check output or behavior

Read the error message as evidence

Order the code reading and verification flow.

**🐞 Defect Birth — if `app.useGlobalFilters(...)` is forgotten** **Code:** the `HttpExceptionFilter` class was written FLAWLESSLY with `@Catch(HttpException)`, `{ error: exception.message }` matches the contract exactly — but `main.ts` has no `app.useGlobalFilters(new HttpExceptionFilter())` call. **What happens:** for a `GET /api/v1/bugs/999` request, `throw new NotFoundException(...)` runs, but since the custom filter is not registered, Nest falls back to its own DEFAULT exception handler — this also returns JSON, but instead of the project contract's `{ error: "..." }`, it returns Nest's own shape: `{ statusCode: 404, message: "...", error: "Not Found" }`. **Why sneaky:** the request still returns JSON (unlike Express's HTML surprise, the server does not look "crashed"), even the 404 status code is correct — but the body's SHAPE differs from what the project expects. A tester checking only the status code never notices this at all. **Where the tester catches it:** writing an assertion that verifies the EXACT SHAPE of the error body (the presence of the `error` field, whether extra fields like `statusCode`/`message` exist) — asking only "is it 404?" is insufficient, the question "does the body MATCH THE CONTRACT?" is mandatory.

🎬 Correct Filter, Unregistered — the Wrong JSON Shape

throw new NotFoundException()

Custom filter: { error }