2.1 Introduction
Middleware are functions that have access to the request (req), response (res), and a special next function in the request-response cycle. Middleware are functions that run before the request reaches the final route handler.
They can:
- Run any code
- Modify
reqorres - End the request-response cycle
- Call
next()to pass control to the next middleware
1. Basics of Middleware
Section titled “1. Basics of Middleware”1.1 🔧 Basic Syntax
Section titled “1.1 🔧 Basic Syntax”function middleware(req, res, next) { // Do something... next(); // Pass to next middleware or route handler}Used like:
app.use(middleware);🧪 Example: Logger Middleware
Section titled “🧪 Example: Logger Middleware”app.use((req, res, next) => { console.log(`${req.method} ${req.url}`); next(); // Continue to next middleware or route});1.2 🛠 Common Built-in Middleware
Section titled “1.2 🛠 Common Built-in Middleware”| Middleware | Purpose |
|---|---|
express.json() | Parses JSON bodies in requests |
express.urlencoded() | Parses URL-encoded form data |
express.static() | Serves static files like HTML, CSS, JS |
1.3 🧭 Where Middleware Runs
Section titled “1.3 🧭 Where Middleware Runs”Middleware can be:
- Application-level – applies to all routes
- Route-level – applies only to specific routes
- Error-handling – catches and handles errors
- Built-in or third-party – like
cors,body-parser,morgan
1.4 🧵 Example Flow
Section titled “1.4 🧵 Example Flow”app.use(express.json()); // 1: parse JSONapp.use(myLogger); // 2: log request
app.post('/data', (req, res) => { res.send('Data received');});1.5 Handling Errors in Middleware
Section titled “1.5 Handling Errors in Middleware”In Express middleware, if there’s an error and you want to stop further processing, you can return a response immediately using res.status(...).send(...) or pass the error to the next error-handling middleware using next(error).
✅ Option 1: Return response directly
Section titled “✅ Option 1: Return response directly”app.use((req, res, next) => { if (!req.headers['x-auth']) { return res.status(401).send('Unauthorized'); } next(); // Call next only if no error});✅ Option 2: Pass error to error-handling middleware
Section titled “✅ Option 2: Pass error to error-handling middleware”app.use((req, res, next) => { if (!req.headers['x-auth']) { const err = new Error('Unauthorized'); err.status = 401; return next(err); } next();});
// Error-handling middleware (must have 4 args)app.use((err, req, res, next) => { res.status(err.status || 500).send(err.message);});1.6 “Headers Already Sent” - Issues
Section titled “1.6 “Headers Already Sent” - Issues”“Headers already sent” is a common error in Express (and other Node.js frameworks) that means:
Your code tried to send an HTTP response more than once, or tried to modify headers after the response body was already started.
❌ Example that causes the error:
Section titled “❌ Example that causes the error:”app.use((req, res, next) => { res.send('Hello'); next(); // ❌ This is called after res.send});Why this is a problem:
Section titled “Why this is a problem:”- When you call
res.send(), Express sends the headers and the body. - HTTP headers must be sent before the body.
- Once the headers are sent, you can’t modify them or send another response.
- If
next()is called after a response was already sent, the next middleware might try to send another response.
✅ Correct way (use return):
Section titled “✅ Correct way (use return):”app.use((req, res, next) => { if (!req.headers['x-auth']) { return res.status(401).send('Unauthorized'); } next();});✅ Also valid (no res.send() called yet):
Section titled “✅ Also valid (no res.send() called yet):”app.use((req, res, next) => { console.log('Still safe to call next()'); next();});2. Middleware Execution Order
Section titled “2. Middleware Execution Order”The order in which middleware runs in Express.js is very important, as it follows a top-to-bottom sequence. The order is determined by the way middleware is registered in your application.
-
Global Middleware
- Middleware registered with
app.use()or specific routes will run in the order they are defined in your code.
- Middleware registered with
-
Route-Specific Middleware
- Middleware attached to specific routes (like
app.get(),app.post(), etc.) runs in the order it is defined for that route.
- Middleware attached to specific routes (like
-
Error Handling Middleware
- Middleware that handles errors is called only if an error is passed to
next(err)within another middleware or route handler. These middleware functions must have 4 arguments:(err, req, res, next).
- Middleware that handles errors is called only if an error is passed to
Key Points:
Section titled “Key Points:”-
1. Order of Definition Matters: Middleware is executed in the order it is defined.
- If you define middleware
app.use()earlier, it runs before middleware defined later.
- If you define middleware
-
2. Call
next()to move forward: After each middleware runs, it needs to callnext()to pass the request to the next middleware or route handler. -
3. If
next()is not called, the request is “stuck” and will not reach the next middleware or route handler.
2.1 Order with Global Middleware
Section titled “2.1 Order with Global Middleware”const express = require('express');const app = express();
// First middlewareapp.use((req, res, next) => { console.log('First middleware'); next(); // Pass to next middleware});
// Second middlewareapp.use((req, res, next) => { console.log('Second middleware'); next(); // Pass to next middleware});
// Route handlerapp.get('/', (req, res) => { res.send('Hello World');});
// Error handling middlewareapp.use((err, req, res, next) => { console.error('Error:', err); res.status(500).send('Something went wrong!');});
app.listen(3000, () => { console.log('Server running on port 3000');});What Happens?
Section titled “What Happens?”- First Middleware → Logs:
First middleware - Second Middleware → Logs:
Second middleware - Route Handler (
GET /) → Sends:Hello World - Error Handling Middleware (if there’s an error) → Catches any errors and responds with a 500 status.
2.2 Order with Route-Specific Middleware
Section titled “2.2 Order with Route-Specific Middleware”You can also apply middleware to specific routes, affecting the sequence for those routes.
app.use((req, res, next) => { console.log('Global Middleware'); next();});
app.get('/home', (req, res, next) => { console.log('Route-Specific Middleware for /home'); next(); // Pass to the next middleware or route handler}, (req, res) => { res.send('Home Page');});
app.listen(3000, () => { console.log('Server running on port 3000');});What Happens?
Section titled “What Happens?”Global Middleware→ Logs:Global MiddlewareRoute-Specific Middleware for /home→ Logs:Route-Specific Middleware for /home- Route Handler for
/home→ Sends:Home Page
2.3 Order with Error Handling Middleware
Section titled “2.3 Order with Error Handling Middleware”If any route or middleware passes an error to next(err), the error handling middleware will run.
app.use((req, res, next) => { // Some logic... next(new Error('Something went wrong!')); // Pass error to error handler});
// Error handling middlewareapp.use((err, req, res, next) => { console.error('Caught an error:', err.message); res.status(500).send('Internal Server Error');});What Happens?
Section titled “What Happens?”- Middleware or route will pass an error via
next(). - The error-handling middleware will catch the error and send an appropriate response.
2.4 Summary
Section titled “2.4 Summary”- Global Middleware: Executed in the order they’re defined with
app.use(). - Route-Specific Middleware: Executed in the order they are defined for specific routes (e.g.,
app.get(),app.post()). - Error Handling Middleware: Activated only when an error is passed using
next(err). It must have 4 parameters:(err, req, res, next).
3. Scope of Middleware
Section titled “3. Scope of Middleware”In Express.js, middleware functions are used to handle requests and responses. There are two main types in terms of scope: application-wide middleware and router-level middleware.
3.1 ✅ Application-wide Middleware
Section titled “3.1 ✅ Application-wide Middleware”These are middleware functions that are applied to the entire app. They run for every incoming request, unless filtered by a path.
💡 How it’s defined:
Section titled “💡 How it’s defined:”const express = require('express');const app = express();
// Application-wide middlewareapp.use((req, res, next) => { console.log('This runs for all routes'); next();});📍 You can also limit it to a specific path:
Section titled “📍 You can also limit it to a specific path:”app.use('/api', (req, res, next) => { console.log('This runs for all routes starting with /api'); next();});3.2 ✅ Router-level Middleware
Section titled “3.2 ✅ Router-level Middleware”This middleware is tied to an instance of express.Router(). It’s like a mini-app with its own middleware stack, and it’s mounted onto the main app at a certain path.
💡 Example:
Section titled “💡 Example:”const express = require('express');const app = express();const router = express.Router();
// Router-level middlewarerouter.use((req, res, next) => { console.log('Router-level middleware'); next();});
// Routes inside the routerrouter.get('/hello', (req, res) => { res.send('Hello from router!');});
// Mount the router at /apiapp.use('/api', router);In this example, router.use() middleware will only apply to routes handled by that router (/api/hello, etc.).
3.3 🔁 Summary Table
Section titled “3.3 🔁 Summary Table”| Middleware Type | Scope | Defined Using | Applied To |
|---|---|---|---|
| Application-wide | Whole app | app.use() | All routes (or filtered by path) |
| Router-level | Specific router | router.use() | Only routes handled by that router |