typescript
45 lines · 6 steps
Catch-all routes and error shaping in NestJS
A wildcard controller turns unmatched routes into 404s, and an exception filter renders every HttpException as consistent JSON.
Explained by
highlit
1import {
2 Controller,
3 All,
4 Req,
5 NotFoundException,
6 HttpException,
7 ExceptionFilter,
8 Catch,
9 ArgumentsHost,
10 HttpStatus,
11} from '@nestjs/common';
12import { Request, Response } from 'express';
13
14@Controller('*')
15export class NotFoundController {
16 @All()
17 handleUnknownRoute(@Req() req: Request): never {
18 throw new NotFoundException(
19 `Cannot ${req.method} ${req.originalUrl}`,
20 );
21 }
22}
23
24@Catch(HttpException)
25export class HttpExceptionFilter implements ExceptionFilter {
26 catch(exception: HttpException, host: ArgumentsHost) {
27 const ctx = host.switchToHttp();
28 const res = ctx.getResponse<Response>();
29 const req = ctx.getRequest<Request>();
30 const status = exception.getStatus();
31 const payload = exception.getResponse();
32
33 res.status(status).json({
34 statusCode: status,
35 error: HttpStatus[status] ?? 'Error',
36 message:
37 typeof payload === 'string'
38 ? payload
39 : (payload as { message?: string | string[] }).message ??
40 exception.message,
41 path: req.originalUrl,
42 timestamp: new Date().toISOString(),
43 });
44 }
45}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A wildcard @Controller('*') gives you a single place to reject every route your app doesn't explicitly handle.
- 2An ExceptionFilter lets you intercept thrown exceptions and control the exact response body clients receive.
- 3Normalizing error payloads to a fixed shape makes APIs predictable for the clients that consume them.
Related explainers
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
java
@RestController @RequestMapping("/api/products") @RequiredArgsConstructor public class ProductBatchController {
Batch JSON Merge Patch in Spring
json-merge-patch
rest-api
partial-update
Intermediate
8 steps
typescript
import { ArgumentsHost, Catch, ConflictException,
Turning TypeORM lock errors into 409s in NestJS
exception-handling
optimistic-locking
http-status
Intermediate
6 steps
java
public class TimedFetchService { private final ExecutorService executor = Executors.newFixedThreadPool(8); private final HttpClient httpClient = HttpClient.newHttpClient();
Enforcing HTTP timeouts with a Future
concurrency
timeouts
thread-pool
Intermediate
8 steps
typescript
import { Component } from '@angular/core'; import { RouterLink, RouterLinkActive } from '@angular/router'; import { NgFor } from '@angular/common';
Building an active-route navbar in Angular
routing
standalone-components
accessibility
Intermediate
6 steps
go
package middleware import ( "net/http"
Role-based access control middleware in Gin
middleware
authorization
closures
Intermediate
7 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/catch-all-routes-and-error-shaping-in-nestjs-explained-typescript-2359/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.