Request Lifecycle
Understanding the order in which NestJS executes its various components is crucial for debugging and architecting a clean application.
Overview
When an HTTP request hits a NestJS application, it goes through a specific sequence of layers before reaching the final route handler (Controller method), and then the response travels back through another sequence before being sent to the client.
If you don’t understand the Request Lifecycle, you will struggle to decide whether a piece of logic belongs in a Guard, an Interceptor, or a Pipe.
The Sequence
Here is the exact order of execution in a NestJS application:
Inbound (Request)
- Middleware
- Global bound middleware
- Module bound middleware
- Guards
- Global guards
- Controller guards
- Route guards
- Interceptors (Pre-Controller)
- Global interceptors
- Controller interceptors
- Route interceptors
- Pipes
- Global pipes
- Controller pipes
- Route pipes
- Route parameter pipes
- Controller (Route Handler)
- The actual method executes, usually delegating to a Service.
Outbound (Response)
- Interceptors (Post-Controller)
- Route interceptors
- Controller interceptors
- Global interceptors
- Exception Filters (if any exceptions were thrown during the cycle)
- Route filters
- Controller filters
- Global filters
Summary of Responsibilities
- Middleware: Best for global things that don’t care about the specific route being executed (e.g., body parsing, logging the raw request, setting security headers).
- Guards: Best for Authentication and Authorization. They execute early and can block a request from proceeding.
- Interceptors: Best for cross-cutting concerns (logging time taken, transforming responses, catching and transforming exceptions). They wrap the execution of the route handler.
- Pipes: Best for data validation and transformation. They run right before the controller and ensure the inputs are correct.
Common Interview Questions
“Why not just use Middleware for Authentication?”
While you can, Guards are preferred. Middleware doesn’t know which specific controller or method will be executed. Guards have access to the ExecutionContext, so they know exactly which route is being called. This allows you to use @Roles() decorators to restrict specific routes to specific roles, which you cannot easily do in generic middleware.
“What’s the difference between a Pipe and an Interceptor?”
- Pipes operate on the arguments being passed to the route handler. They can only transform or validate the input.
- Interceptors wrap the entire execution of the route handler. They can access the request before the handler runs, AND they can manipulate the response (or exception) returned by the handler.