0
0
NextJSframework~15 mins

Middleware for API routes in NextJS - Deep Dive

Choose your learning style9 modes available
Overview - Middleware for API routes
What is it?
Middleware for API routes in Next.js is a special function that runs before your API route handler. It can inspect, modify, or stop requests and responses. This helps you add features like authentication, logging, or input validation easily. Middleware acts like a checkpoint for every API call.
Why it matters
Without middleware, you would have to repeat the same code in every API route, making your app harder to maintain and more error-prone. Middleware lets you write common logic once and apply it everywhere, saving time and reducing bugs. It also helps keep your API routes clean and focused on their main job.
Where it fits
Before learning middleware, you should understand basic Next.js API routes and how HTTP requests work. After mastering middleware, you can explore advanced topics like custom server setups, edge functions, or integrating third-party services in your API.
Mental Model
Core Idea
Middleware is a gatekeeper function that runs before your API route to control or enhance requests and responses.
Think of it like...
Middleware is like a security guard at a building entrance who checks IDs, logs visitors, or redirects people before they enter the main office.
┌───────────────┐
│ Incoming HTTP │
│   Request     │
└──────┬────────┘
       │
┌──────▼───────┐
│  Middleware  │
│ (checks,     │
│  modifies,   │
│  blocks)     │
└──────┬───────┘
       │
┌──────▼───────┐
│ API Route    │
│  Handler     │
└──────┬───────┘
       │
┌──────▼───────┐
│ HTTP Response│
└──────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Next.js API Routes
🤔
Concept: Learn what API routes are and how they handle HTTP requests in Next.js.
Next.js API routes let you create backend endpoints inside your Next.js app. Each file inside the /pages/api folder exports a function that receives a request and response object. This function runs when someone calls that endpoint, like /api/hello. You write code here to handle GET, POST, or other HTTP methods.
Result
You can create simple API endpoints that respond with data or perform actions when called.
Knowing how API routes work is essential because middleware runs before these handlers and can affect their behavior.
2
FoundationWhat Middleware Does in API Routes
🤔
Concept: Middleware runs before the API route handler to inspect or change the request or response.
Middleware is a function that receives the request and response objects, plus a next function to continue. It can check if a user is logged in, add headers, or stop the request with an error. If middleware calls next(), the API route handler runs next. If not, the request ends early.
Result
Middleware can block unauthorized requests or add info before the main API code runs.
Middleware helps separate concerns by handling common tasks outside the main API logic.
3
IntermediateCreating Middleware Functions
🤔Before reading on: Do you think middleware must always call next() to work correctly? Commit to your answer.
Concept: How to write middleware functions that control the flow of API requests.
A middleware function takes three arguments: req, res, and next. Inside, you can check request data or headers. If everything is okay, call next() to continue. If not, send a response to stop the request. Example: function authMiddleware(req, res, next) { if (!req.headers.authorization) { res.status(401).json({ error: 'Unauthorized' }); } else { next(); } }
Result
Requests without authorization headers get blocked; others proceed to the API handler.
Understanding when and how to call next() controls whether the API route runs or the request stops early.
4
IntermediateApplying Middleware to API Routes
🤔Before reading on: Do you think middleware is automatically applied to all API routes in Next.js? Commit to your answer.
Concept: Learn how to use middleware functions in your API route files.
Middleware is not automatic in Next.js API routes. You must call it explicitly inside your API handler or wrap your handler with middleware. For example: import authMiddleware from './authMiddleware'; export default function handler(req, res) { authMiddleware(req, res, () => { res.status(200).json({ message: 'Success' }); }); } Or use a helper to compose middleware with handlers.
Result
Middleware runs before the API handler only when you call it, giving you control over which routes use it.
Middleware is flexible and can be applied selectively, avoiding unnecessary checks on every route.
5
IntermediateComposing Multiple Middleware Functions
🤔Before reading on: Do you think multiple middleware functions run in parallel or in sequence? Commit to your answer.
Concept: How to run several middleware functions one after another before the API handler.
You can chain middleware by calling each one inside the next callback. For example: function mw1(req, res, next) { console.log('mw1'); next(); } function mw2(req, res, next) { console.log('mw2'); next(); } export default function handler(req, res) { mw1(req, res, () => { mw2(req, res, () => { res.status(200).json({ message: 'Done' }); }); }); } This runs mw1, then mw2, then the handler.
Result
Middleware functions run in order, each able to modify the request or response before the next runs.
Knowing middleware runs sequentially helps you design clear, predictable request processing pipelines.
6
AdvancedUsing Middleware with Async/Await
🤔Before reading on: Can middleware functions be asynchronous and still control flow correctly? Commit to your answer.
Concept: Middleware can be async functions that await operations like database checks before continuing.
You can write middleware as async functions: async function authMiddleware(req, res, next) { const user = await getUserFromToken(req.headers.authorization); if (!user) { res.status(401).json({ error: 'Unauthorized' }); } else { req.user = user; next(); } } This lets you perform async tasks before the API handler runs.
Result
Middleware can handle real-world async checks like authentication or data fetching before the main logic.
Async middleware unlocks powerful capabilities but requires careful flow control to avoid hanging requests.
7
ExpertMiddleware Internals and Edge Cases
🤔Before reading on: Do you think middleware can modify the response after the API handler sends it? Commit to your answer.
Concept: Explore how middleware interacts with the request-response lifecycle and common pitfalls.
Middleware runs before the API handler and can modify req and res objects. However, once the API handler sends a response (res.end or res.json), middleware cannot change it. Also, if middleware forgets to call next() or send a response, the request hangs. Middleware must be carefully designed to avoid these issues. In Next.js, middleware is synchronous or async but must always end the request or call next().
Result
Middleware controls the start of request handling but cannot change responses after they are sent.
Understanding middleware lifecycle limits prevents bugs like hanging requests or ignored modifications.
Under the Hood
Middleware functions run in sequence before the API route handler. Each middleware receives the request and response objects and a next function. Calling next() passes control to the next middleware or the API handler. If a middleware sends a response or does not call next(), the chain stops. Internally, this is a simple function call chain that modifies the same request and response objects, allowing shared state and control flow.
Why designed this way?
Middleware was designed to separate concerns and reuse common logic across routes. The next() pattern comes from popular server frameworks like Express.js, providing a simple way to chain functions. This design avoids duplicating code and keeps API handlers focused. Alternatives like event emitters or promises were less intuitive or harder to control flow with, so the next() callback became the standard.
┌───────────────┐
│ HTTP Request  │
└──────┬────────┘
       │
┌──────▼───────┐
│ Middleware 1 │
│ (calls next) │
└──────┬───────┘
       │
┌──────▼───────┐
│ Middleware 2 │
│ (calls next) │
└──────┬───────┘
       │
┌──────▼───────┐
│ API Handler  │
│ (sends resp) │
└──────┬───────┘
       │
┌──────▼───────┐
│ HTTP Response│
└──────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does middleware automatically run on all Next.js API routes? Commit to yes or no.
Common Belief:Middleware runs automatically on every API route without extra setup.
Tap to reveal reality
Reality:In Next.js API routes, middleware must be explicitly called or composed; it does not run automatically.
Why it matters:Assuming automatic middleware leads to missing critical checks or duplicated code, causing security or maintenance issues.
Quick: Can middleware modify the response after the API handler sends it? Commit to yes or no.
Common Belief:Middleware can change the response anytime, even after the API handler finishes.
Tap to reveal reality
Reality:Once the API handler sends a response, middleware cannot modify it because the HTTP response is already sent.
Why it matters:Trying to modify a sent response causes errors or silent failures, confusing developers and users.
Quick: Do multiple middleware functions run at the same time or one after another? Commit to your answer.
Common Belief:Middleware functions run in parallel to speed up processing.
Tap to reveal reality
Reality:Middleware functions run sequentially, each waiting for the previous to call next() before running.
Why it matters:Misunderstanding this can cause race conditions or unexpected behavior in request handling.
Quick: Is it okay for middleware to forget calling next() or sending a response? Commit to yes or no.
Common Belief:Middleware can skip calling next() or sending a response without problems.
Tap to reveal reality
Reality:If middleware neither calls next() nor sends a response, the request hangs and never finishes.
Why it matters:This causes poor user experience and server resource leaks, making apps unreliable.
Expert Zone
1
Middleware can share data by attaching properties to the req object, enabling communication between middleware and handlers.
2
Error handling in middleware requires careful design; throwing errors or passing them to next(err) is not built-in in Next.js API routes, so you must handle errors explicitly.
3
Middleware order matters deeply; placing authentication after logging can leak sensitive info, so plan middleware sequence carefully.
When NOT to use
Middleware is not ideal for very simple API routes with no shared logic or when using Next.js Edge Middleware for global request handling. For complex workflows, consider dedicated backend services or serverless functions with built-in middleware support.
Production Patterns
In production, middleware is used for authentication, rate limiting, input validation, and logging. Developers often create reusable middleware libraries and compose them with helper functions to keep API routes clean and maintainable.
Connections
Express.js Middleware
Middleware in Next.js API routes is inspired by and similar to Express.js middleware patterns.
Understanding Express.js middleware helps grasp Next.js middleware flow and common patterns like next() callbacks.
HTTP Request Lifecycle
Middleware operates during the HTTP request lifecycle before the final response is sent.
Knowing the HTTP lifecycle clarifies when middleware runs and why it cannot modify responses after sending.
Assembly Line in Manufacturing
Middleware chaining is like an assembly line where each station adds or checks something before passing the product along.
Seeing middleware as an assembly line helps understand sequential processing and the importance of order.
Common Pitfalls
#1Middleware forgets to call next() or send a response.
Wrong approach:function middleware(req, res, next) { if (!req.headers.auth) { res.status(401).json({ error: 'No auth' }); } // missing next() call here }
Correct approach:function middleware(req, res, next) { if (!req.headers.auth) { res.status(401).json({ error: 'No auth' }); } else { next(); } }
Root cause:Not calling next() or sending a response leaves the request hanging with no end.
#2Assuming middleware runs automatically on all API routes.
Wrong approach:export default function handler(req, res) { res.status(200).json({ message: 'Hello' }); } // no middleware call anywhere
Correct approach:import middleware from './middleware'; export default function handler(req, res) { middleware(req, res, () => { res.status(200).json({ message: 'Hello' }); }); }
Root cause:Middleware must be explicitly called; otherwise, it never runs.
#3Trying to modify response after it is sent.
Wrong approach:export default function handler(req, res) { res.status(200).json({ message: 'Done' }); middleware(req, res, () => {}); // too late }
Correct approach:export default function handler(req, res) { middleware(req, res, () => { res.status(200).json({ message: 'Done' }); }); }
Root cause:Response must be sent once after middleware finishes; modifying after sending is impossible.
Key Takeaways
Middleware in Next.js API routes runs before the main handler to control or enhance requests and responses.
Middleware must be explicitly called in API routes; it does not run automatically.
Middleware functions run sequentially and must call next() or send a response to avoid hanging requests.
Async middleware enables powerful checks like authentication but requires careful flow control.
Understanding middleware lifecycle limits prevents common bugs like modifying sent responses or hanging requests.