NestJS Request Lifecycle: What Happens Before and After Your Controller


Once you understand modules, controllers, providers, dependency injection, TypeScript, and decorators, the next big question is:

What actually happens when a request enters a NestJS app?

Because this is the part where Nest can start feeling slightly magical again.

You hear terms like:

and very quickly it can feel like your request is being passed through a secret underground tunnel system before it ever reaches your controller.

The good news is that the flow is actually pretty logical.

And once you understand the request lifecycle, a lot of NestJS starts to click in a much deeper way.

In this article, we will make that flow simple.

By the end, you will understand:

Let’s build the mental flowchart.


Why the Request Lifecycle Matters

You can build a few NestJS routes without fully understanding the lifecycle.

But at some point, confusion starts showing up.

For example:

These questions are all lifecycle questions.

And if you understand the request lifecycle, you stop guessing where things belong.

That makes your code cleaner, and honestly, it saves a lot of unnecessary framework confusion.


The High-Level NestJS Request Flow

Nest’s official request lifecycle guide says the general flow is:

middleware → guards → interceptors → pipes → controller/route handler → back through interceptors on the response path.

That is the main shape.

In plain English:

  1. the request enters the app
  2. middleware runs first
  3. guards decide whether the request is allowed
  4. interceptors can wrap what happens next
  5. pipes transform or validate incoming data
  6. the controller method runs
  7. the response goes back out, often through interceptors again
  8. if something throws, exception handling steps in

That is the flow.

Already less scary, right?


A Simple Mental Model

If you want the beginner-friendly version, think of it like this:

That model is not academically fancy.

But it is very useful.

And useful beats fancy.


Step 1: Middleware Runs First

Nest’s middleware docs say middleware runs before the route handler and has access to the request, response, and the next() function in the request-response cycle. The request lifecycle FAQ also notes that globally bound middleware runs before module-bound middleware, and middleware runs in binding order.

So middleware is the first thing that gets a shot at the request.

What Middleware Is Good For

Middleware is useful for things like:

What Middleware Is Not Great For

Middleware is usually not the best place for:

Why?

Because middleware runs early and does not know much about which specific controller method is about to execute. Guards, on the other hand, do know the execution context of the next handler. :contentReference[oaicite:3]{index=3}

Example

A simple logging middleware might:

That is a good fit.

Trying to make middleware handle all role-based permission logic?

Usually not a great fit.


Step 2: Guards Decide Whether the Request Can Continue

Nest’s guards docs say guards have a single responsibility: they determine whether a given request will be handled by the route handler, depending on runtime conditions like roles, permissions, or other auth rules.

This is why guards are such a natural fit for:

Why Guards Are Better Than Middleware for Auth Decisions

Middleware can inspect the request.

But guards know more.

They have access to the ExecutionContext, which means they know what handler and route are about to run. Nest explicitly calls this out as a key reason guards are more appropriate than middleware for authorization-style logic.

That makes guards much better for questions like:

Simple Guard Mental Model

A guard basically asks:

“Should this request be allowed to continue?”

If yes, the flow continues.
If no, the handler never runs.

Which is why guard failures often feel dramatic.

Because they are.


Step 3: Interceptors Wrap the Route Execution

Nest’s interceptor docs describe interceptors as tools inspired by AOP that can run logic before and after method execution, transform returned data, transform thrown exceptions, extend behavior, or even completely override a function in certain cases.

This makes interceptors extremely powerful.

What Interceptors Are Good For

Interceptors are great for:

Why Interceptors Feel Special

Interceptors are different from middleware and guards because they wrap the actual execution.

That means they can:

So if middleware is the front door, interceptors are more like a smart wrapper around the whole controller action.

That is why they are so useful.

A Very Common Example

A response interceptor might turn this:

{ "id": 1, "name": "Alice" }

into this:

{ "data": { "id": 1, "name": "Alice" } }

Now every response follows a consistent format.

That is a classic interceptor job.


Step 4: Pipes Validate or Transform Data

Nest’s pipes docs say pipes operate on the arguments being processed by a controller route handler, and Nest places a pipe just before a method is invoked. Pipes are mainly for transformation and validation. They also run inside the exceptions zone, so if a pipe throws, the controller method does not execute.

This is one of the most important lifecycle details for beginners.

What Pipes Are Good For

Pipes are perfect for:

Why Pipes Belong So Close to the Controller

Pipes work on the actual method arguments.

That means they are not just dealing with the raw request in a generic way.

They are dealing with the exact data that will be passed into your controller method.

That is why they are a much better fit than middleware for validation.

Example

If your route expects an id as a number, a pipe can:

Very useful.
Very focused.
Very much not a middleware job.


Step 5: The Controller Route Handler Runs

Nest’s controller docs still define controllers as the classes responsible for handling incoming requests and sending responses back to the client.

This is the part most beginners understand first, because it looks the most familiar.

You define a route like:

@Get(':id')
findOne(@Param('id') id: string) {
  return this.usersService.findOne(id);
}

By the time your controller method runs:

That means the controller should get cleaner, safer data.

Which is great.

Because controllers should focus on:

Not doing every job in the framework all by themselves.


Step 6: The Response Travels Back Out Through Interceptors

This is one of the trickiest parts for beginners.

Interceptors are involved on the way in and also on the way out.

Nest’s request lifecycle FAQ explicitly says the request goes to interceptors before pipes and then returns back through interceptors as the response is generated. The interceptor docs also explain they can transform the result returned from a function.

That is why interceptors are such a natural place for:

So if you are wondering why response wrapping is usually done in an interceptor instead of a controller, this is the reason.

Interceptors sit in exactly the right place.


Where Exception Filters Fit In

Exception filters are the part that handles thrown exceptions in a more structured way. Pipes also run inside the exceptions zone, which means errors thrown there are handled by the exceptions layer too.

Here is the simple version:

if something goes wrong in the lifecycle, exception handling steps in

That error might come from:

Exception filters help turn those errors into cleaner, more controlled responses.

What Exception Filters Are Good For

They are useful for:

So while exception filters are not usually presented as one of the main forward steps in the request flow, they are absolutely part of the request lifecycle story.

They are the cleanup crew when something throws.


The Real Beginner Question: Which Tool Should I Use?

This is where people get stuck.

So here is the simplest version.

Use Middleware When

Use Guards When

Use Pipes When

Use Interceptors When

Use Exception Filters When

That is the practical cheat sheet.


A Full Example Flow

Let’s imagine a request comes in:

GET /users/42
Authorization: Bearer token

Here is how the lifecycle might look:

1. Middleware

A logger middleware records:

2. Guard

An auth guard checks the token.
If the token is invalid, the request stops here.

3. Interceptor

A timing interceptor starts measuring how long the request takes.

4. Pipe

A pipe validates that 42 is a valid numeric ID.

5. Controller

The controller method receives the cleaned input and calls the service.

6. Service / Provider

The provider fetches the user.

7. Interceptor Again

The interceptor wraps the response and logs execution time.

8. Final Response

The client receives a clean response.

If an error happens at one of those stages, exception handling shapes the failure response.

That is the lifecycle in action.


Why Understanding This Makes You Better at NestJS

A lot of NestJS confusion is not really about syntax.

It is about putting logic in the wrong place.

For example:

Once you understand the lifecycle, these decisions get easier.

And your code starts feeling more “Nest-like.”

Which usually means:

That is a very good upgrade.


Common Beginner Mistakes

1. Treating Middleware and Guards as the Same Thing

They are not.

Middleware runs early and is more generic.
Guards decide whether the route handler should execute, and they know the execution context.

2. Doing Validation in Controllers

You can, but that is usually a sign pipes should be doing the job.

3. Forgetting That Interceptors Work on the Way Back Too

This is why they are so useful for response formatting.

4. Putting Too Much in One Layer

If your middleware logs, authenticates, validates, transforms responses, and handles errors, it is trying to live five different lives at once.

That is not ideal.

5. Ignoring Exception Handling Until Later

This often works fine until it very much does not.


A Good Mental Shortcut to Remember

Here is the short version to keep in your head:

If that sentence sticks, you are already in good shape.


Final Thoughts

The NestJS request lifecycle sounds complicated at first because there are several moving pieces.

But the logic behind it is actually pretty clean.

A request comes in.
Middleware sees it first.
Guards decide whether it is allowed.
Interceptors wrap what happens next.
Pipes validate and transform input.
The controller runs.
The response comes back out, often through interceptors again.
And if something fails, exception handling keeps things under control.

Once you understand that flow, a lot of NestJS starts feeling much more intentional.

And that is exactly what we want.

Because the next step is diving into one of the most practical layers in that flow:

pipes and DTOs

That is where you will really start making your input handling cleaner, safer, and much less chaotic.


Real Interview Questions

What is the NestJS request lifecycle?

It is the sequence a request follows inside a NestJS application, generally moving through middleware, guards, interceptors, pipes, the controller/handler, and then back through interceptors on the response path.

Do pipes run before or after the controller?

Before. Nest places pipes just before the route handler method is invoked.

What is the difference between middleware and guards in NestJS?

Middleware runs early and is more generic. Guards specifically decide whether the request should be handled by the route handler and have access to the execution context.

Why are interceptors useful in NestJS?

Because they can wrap the execution, run logic before and after the handler, and transform returned data or exceptions.

Where do exception filters fit in NestJS?

They handle thrown exceptions in a structured way, helping standardize error responses across the app. Pipes also run inside the exceptions zone, so errors thrown there are handled by the exceptions layer.