0
0
NestJSframework~15 mins

Creating middleware in NestJS - Mechanics & Internals

Choose your learning style9 modes available
Overview - Creating middleware
What is it?
Middleware in NestJS is a function that runs before the route handler. It can modify the request or response objects or end the request-response cycle. Middleware helps add common logic like logging, authentication, or data parsing to many routes without repeating code. It acts as a checkpoint for incoming requests.
Why it matters
Without middleware, you would have to write the same code inside every route handler, making your app messy and hard to maintain. Middleware lets you keep your code clean and organized by handling shared tasks in one place. This saves time and reduces bugs, making your app more reliable and easier to update.
Where it fits
Before learning middleware, you should understand basic NestJS concepts like modules, controllers, and providers. After mastering middleware, you can explore advanced topics like guards, interceptors, and exception filters to control request flow and error handling.
Mental Model
Core Idea
Middleware is a gatekeeper function that processes requests before they reach the main route handler.
Think of it like...
Middleware is like a security guard at a building entrance who checks visitors before letting them inside. The guard can ask questions, check IDs, or stop people from entering if needed.
Incoming Request
     ↓
┌───────────────┐
│   Middleware  │  → modifies or blocks request
└───────────────┘
     ↓
┌───────────────┐
│ Route Handler │  → processes request and sends response
└───────────────┘
     ↓
Outgoing Response
Build-Up - 6 Steps
1
FoundationWhat is Middleware in NestJS
🤔
Concept: Middleware is a function that runs before route handlers to process requests.
In NestJS, middleware functions receive the request and response objects and a next function. They can read or change the request, perform actions like logging, and then call next() to pass control to the next middleware or route handler.
Result
Middleware runs automatically on matching routes before the main handler.
Understanding middleware as a pre-route step helps you see how to insert shared logic without repeating code.
2
FoundationHow to Create Basic Middleware
🤔
Concept: Middleware is created as a class implementing NestJS's MiddlewareInterface.
Create a class with a method called 'use' that takes request, response, and next. Inside 'use', add your logic and call next() to continue. Then, apply this middleware in a module to specific routes.
Result
Middleware class runs its 'use' method on requests to configured routes.
Knowing the class structure and 'use' method is key to writing middleware that fits NestJS patterns.
3
IntermediateApplying Middleware to Routes
🤔Before reading on: Do you think middleware applies globally or only to routes you specify? Commit to your answer.
Concept: Middleware can be applied globally or to specific routes using the module's configure method.
In your module, implement the 'configure' method from NestModule. Use 'consumer.apply(MyMiddleware).forRoutes('path')' to apply middleware only to certain routes. You can also use '*' to apply to all routes.
Result
Middleware runs only on routes you specify, giving control over where logic applies.
Knowing how to target middleware prevents unnecessary processing and improves app performance.
4
IntermediateUsing Middleware for Common Tasks
🤔Before reading on: Can middleware modify the request object for later handlers? Commit to yes or no.
Concept: Middleware can add or change data on the request object for use in route handlers.
For example, middleware can add a 'user' property to the request after verifying a token. Later, route handlers can read 'request.user' to know who is making the request.
Result
Middleware enriches requests with useful data, enabling cleaner route handlers.
Understanding request modification unlocks powerful patterns like authentication and data validation.
5
AdvancedMiddleware Execution Order and Stacking
🤔Before reading on: If multiple middleware apply to a route, do they run in the order declared or reverse? Commit to your answer.
Concept: Middleware runs in the order they are applied, one after another, before the route handler.
When you apply multiple middleware, NestJS calls them sequentially. Each must call next() to pass control. If one middleware does not call next(), the chain stops and the request ends there.
Result
Middleware stacking allows building complex request processing pipelines.
Knowing execution order helps avoid bugs where middleware unintentionally blocks requests.
6
ExpertMiddleware vs Guards and Interceptors
🤔Before reading on: Do you think middleware can handle authorization as well as guards? Commit to yes or no.
Concept: Middleware runs before guards and interceptors and is best for low-level tasks, while guards handle authorization and interceptors handle response transformation.
Middleware is good for tasks like logging, parsing, or adding data. Guards decide if a request can proceed based on roles or permissions. Interceptors modify responses or handle errors. Mixing these correctly leads to clean, maintainable code.
Result
Clear separation of concerns improves app security and maintainability.
Understanding the distinct roles of middleware, guards, and interceptors prevents misuse and design mistakes.
Under the Hood
NestJS middleware are functions or classes that the framework calls during the HTTP request lifecycle before reaching controllers. Internally, NestJS uses Express or Fastify under the hood, which handle middleware as a chain of functions. Each middleware receives the request and response objects and a next callback. Calling next() passes control to the next middleware or route handler. If next() is not called, the request stops there. NestJS wraps this native middleware mechanism in a class-based interface for consistency and dependency injection.
Why designed this way?
NestJS designed middleware to align with popular Node.js frameworks like Express, making it familiar and compatible. Using classes and interfaces fits NestJS's modular and injectable architecture, allowing middleware to use services and other dependencies easily. This design balances flexibility with structure, enabling developers to write reusable, testable middleware while leveraging existing Node.js middleware patterns.
Incoming HTTP Request
     ↓
┌─────────────────────┐
│ NestJS Middleware   │
│ (class with 'use')  │
└─────────┬───────────┘
          ↓ calls next()
┌─────────┴───────────┐
│ Next Middleware or  │
│ Route Handler       │
└─────────┬───────────┘
          ↓
┌─────────┴───────────┐
│ Response Sent Back  │
└─────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does middleware automatically handle errors if something goes wrong? Commit to yes or no.
Common Belief:Middleware automatically catches and handles errors in the request pipeline.
Tap to reveal reality
Reality:Middleware does not handle errors unless explicitly coded to do so. Errors must be passed to error-handling middleware or caught elsewhere.
Why it matters:Assuming middleware handles errors can cause unhandled exceptions and crash the app.
Quick: Can middleware modify the response after the route handler sends it? Commit to yes or no.
Common Belief:Middleware can change the response after the route handler finishes processing.
Tap to reveal reality
Reality:Middleware runs before the route handler and cannot modify the response after it is sent. For response modification, use interceptors.
Why it matters:Trying to modify response in middleware leads to bugs and unexpected behavior.
Quick: If middleware forgets to call next(), does the request continue? Commit to yes or no.
Common Belief:If middleware does not call next(), the request still proceeds to the route handler.
Tap to reveal reality
Reality:If next() is not called, the request stops and the client waits indefinitely or times out.
Why it matters:Forgetting next() causes requests to hang, making the app unresponsive.
Quick: Is middleware the best place to check user permissions? Commit to yes or no.
Common Belief:Middleware is the ideal place to handle user authorization and permissions.
Tap to reveal reality
Reality:Guards are designed for authorization logic; middleware is better for generic tasks like logging or parsing.
Why it matters:Using middleware for authorization can lead to security holes and harder-to-maintain code.
Expert Zone
1
Middleware can be asynchronous and return promises, but forgetting to await or call next() properly causes subtle bugs.
2
Middleware ordering affects performance and behavior; placing heavy middleware globally can slow all requests unnecessarily.
3
Middleware can share state via request properties, but this must be done carefully to avoid conflicts or leaks.
When NOT to use
Avoid using middleware for authorization or response transformation; use guards and interceptors instead. Also, do not use middleware for error handling; use dedicated error filters. For complex request lifecycle control, prefer NestJS's built-in guards and interceptors.
Production Patterns
In production, middleware is often used for logging requests, parsing JSON bodies, handling CORS, and adding security headers. Middleware chaining is carefully ordered to optimize performance. Authorization is handled by guards, while interceptors manage response formatting and caching.
Connections
HTTP Request Lifecycle
Middleware is an early step in the HTTP request lifecycle before controllers.
Understanding middleware clarifies how requests flow through a server and where to insert logic.
Express Middleware
NestJS middleware builds on Express middleware patterns but adds class-based structure.
Knowing Express middleware helps grasp NestJS middleware faster and leverage existing middleware packages.
Assembly Line Manufacturing
Middleware is like stations on an assembly line, each adding or checking something before the product moves on.
Seeing middleware as a stepwise process helps design clear, maintainable request handling.
Common Pitfalls
#1Middleware does not call next(), causing requests to hang.
Wrong approach:use(req, res, next) { console.log('Request received'); // forgot to call next() }
Correct approach:use(req, res, next) { console.log('Request received'); next(); }
Root cause:Misunderstanding that next() must be called to continue the request chain.
#2Trying to modify response after route handler in middleware.
Wrong approach:use(req, res, next) { next(); res.send('Modified response'); }
Correct approach:use(req, res, next) { res.setHeader('X-Custom', 'value'); next(); }
Root cause:Not realizing middleware runs before the route handler sends the response.
#3Using middleware for authorization logic instead of guards.
Wrong approach:use(req, res, next) { if (!req.user.isAdmin) { res.status(403).send('Forbidden'); } else { next(); } }
Correct approach:Implement a guard that checks user roles and returns true or false to allow or deny access.
Root cause:Confusing middleware's role with guards designed for authorization.
Key Takeaways
Middleware in NestJS runs before route handlers to process requests and add shared logic.
Middleware is implemented as classes with a 'use' method and applied to routes via the module's configure method.
Middleware can modify the request object but cannot change the response after it is sent.
Proper middleware ordering and calling next() are essential to avoid request hangs and bugs.
Middleware is best for generic tasks like logging and parsing; use guards and interceptors for authorization and response handling.