Fullstack CourseLearn by building
Back to week 3

Topic

Middleware vs guards vs pipes

Definition

Nest processes an incoming request through ordered extension points — middleware, guards, interceptors, and pipes — where each stage has full access to different information and a distinct, narrow responsibility.

In simpler words

Middleware runs first and knows nothing about routes; guards decide who may proceed; pipes clean up and validate the data the handler will receive.

Correlation ids, JWT/role checks, and DTO validation look similar — “code that runs before my controller” — but belong in different stages for a reason.

After this you can

  • Order middleware, guards, and pipes in the request pipeline
  • Explain why middleware cannot read @Roles or @Public
  • Choose the right stage for a new cross-cutting check

Where each stage sits in the pipeline

Definition

Middleware executes before Nest resolves a route handler and its metadata, guards execute afterward with full execution-context access, and pipes execute per parameter to transform or validate arguments just before the handler runs.

In simpler words

Middleware cannot see @Roles or @Public; guards can. Pipes see one argument at a time, not the whole request.

Rough order: Middleware → Guards → Interceptors (before) → Pipes → Controller handler → Interceptors (after) → Exception filters.

CorrelationIdMiddleware runs for every matched path via consumer.apply(...).forRoutes("*") — it cannot make an authorization decision because guards, and the route metadata they read, do not exist yet at that point.

Correlation id middleware

const correlationId = req.header('x-correlation-id') ?? randomUUID();
req.correlationId = correlationId;
res.setHeader('x-correlation-id', correlationId);
next();

Cross-cutting and stateless — a good middleware job. It never reads @Roles or user identity.

Choosing the right tool for a new check

Definition

A new cross-cutting request concern belongs in middleware only when it needs no route metadata and no per-argument transformation, in a guard when it depends on route metadata or the authenticated subject, and in a pipe when it validates or reshapes one specific argument.

In simpler words

Ask: does this need the route’s decorators (guard), just one incoming value (pipe), or neither (middleware)?

ValidationPipe is registered globally in main.ts and runs for every DTO-typed parameter — it is what turns CreateTicketDto’s decorators into an actual 400 response.

ParseUUIDPipe on :id params is a pipe too: it rejects a malformed id before TicketsService ever runs a query.

Three stages, one request

GET /tickets/not-a-uuid
// Middleware: attaches correlation id, calls next()
// Guards: JwtAuthGuard confirms the session, RolesGuard allows (no @Roles here)
// Pipe: ParseUUIDPipe rejects 'not-a-uuid' -> 400 before the handler runs

Each stage rejects or passes the request for a different reason — none of them duplicate the others’ job.

Keep in mind

  • Middleware: cross-cutting, stateless, no route metadata.
  • Guards: authentication and authorization, route-metadata aware.
  • Pipes: per-argument validation and transformation, closest to the handler.

Test

Check your understanding

At least 10 questions — mix of concept, syntax, practical, and logic. Score ≥ 80% (enforced by the API) to save progress.

Checking your session…

10 questions · concept 3 · syntax 2 · practical 3 · logic 2

Concept1. Which statement best defines Middleware vs guards vs pipes?
Syntax2. Which code-level choice matches Middleware vs guards vs pipes?
Practical3. A reviewer spots a bug related to Middleware vs guards vs pipes. What is the right fix?
Logic4. Which reasoning correctly explains Middleware vs guards vs pipes?
Concept5. Which boundary does correct use of Middleware vs guards vs pipes preserve?
Practical6. What is the safest next step when applying Middleware vs guards vs pipes?
Syntax7. Which implementation direction matches the rule for Middleware vs guards vs pipes?
Logic8. Which consequence follows from applying Middleware vs guards vs pipes correctly?
Concept9. Which claim about Middleware vs guards vs pipes is true in this Nest + Postgres monorepo?
Practical10. Which team practice best demonstrates Middleware vs guards vs pipes?