Express.js Middleware
# Express.js Middleware
Welcome to Chapter 15! If you want to truly master Express.js, you must understand Middleware. It is the most important concept in the framework.
Imagine a bouncer at a nightclub. Before you can get to the dance floor (the route), the bouncer (the middleware) checks your ID. If you're old enough, he lets you in. If not, he rejects you. Middleware functions are just like that bouncer—they stand between the incoming request and the final route response.
---
1. Introduction
Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application's request-response cycle.
Middleware can:
- 1. Execute any code.
- 2. Make changes to the request and the response objects.
- 3. End the request-response cycle (e.g., send an error).
-
4.
Call the
next()middleware in the stack.
If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will hang permanently.
---
2. Learning Objectives
By the end of this chapter, you will be able to:
- Define what middleware is in the context of Express.
- Write custom middleware functions.
-
Understand the importance of the
next()function.
-
Apply middleware globally using
app.use().
- Apply middleware locally to specific routes.
- Build a custom request logger middleware.
---
3. Beginner-Friendly Explanations
The Request Pipeline
When a request hits your server, it enters a "pipeline."- 1. Request arrives.
-
2.
Goes through Middleware 1 (e.g., checks if the user is logged in). Calls
next().
-
3.
Goes through Middleware 2 (e.g., logs the time of the request). Calls
next().
-
4.
Reaches the Route Handler (
app.get). Sends theres.send().
If Middleware 1 realizes the user is NOT logged in, it can use res.send("Access Denied") and simply *not* call next(). The pipeline stops, and the route is protected!
---
4. Syntax Explanation
Let's write the simplest possible middleware function.
```javascript id="ch15-syntax-1" const express = require('express'); const app = express();
// A custom middleware function const myLogger = (req, res, next) => { console.log('A new request passed through the middleware!'); // CRITICAL: You must call next(), or the app freezes here. next(); };
// Apply it globally to ALL routes app.use(myLogger);
app.get('/', (req, res) => { res.send('Hello World!'); // This runs AFTER myLogger });
app.listen(3000);
javascript id="ch15-code-1" const checkAdmin = (req, res, next) => { // Simulating an auth check const isAdmin = false; if (isAdmin) { next(); // Let them through } else { res.status(403).send('Forbidden: You are not an admin.'); } };
// Route without middleware app.get('/public', (req, res) => res.send('Public Page'));
// Route WITH middleware inserted as the 2nd argument! app.get('/dashboard', checkAdmin, (req, res) => { res.send('Welcome to the secret admin dashboard!'); });
javascript id="ch15-code-2" const addTimestamp = (req, res, next) => { // Adding a custom property to the req object req.requestTime = new Date().toLocaleTimeString(); next(); };
app.use(addTimestamp);
app.get('/', (req, res) => {
// We can access the property created by the middleware!
res.send(You requested this page at: ${req.requestTime});
});
javascript id="ch15-code-3" // This built-in middleware tells Express to automatically // serve any files placed in the 'public' folder. app.use(express.static('public'));
javascript id="ch15-mini-project" // loggerApp.js const express = require('express'); const app = express();
// 1. Define the Custom Middleware
const requestLogger = (req, res, next) => {
const time = new Date().toISOString();
const method = req.method;
const url = req.url;
console.log([${time}] ${method} request to ${url});
// Pass control to the next function
next();
};
// 2. Apply Middleware Globally (Must be at the top!) app.use(requestLogger);
// 3. Define Routes app.get('/', (req, res) => { res.send("<h1>Home Page</h1>"); });
app.get('/products', (req, res) => { res.send("<h1>Products Page</h1>"); });
// 4. Start Server
app.listen(3000, () => {
console.log('Server running. Check terminal for logs.');
});
``
Run it:
node loggerApp.js
Visit /, then visit /products, then visit /fake-page. Watch your terminal log every single interaction!
---
12. Coding Challenges
Challenge 1: Create an ageRestriction middleware. It should check if req.query.age is greater than or equal to 18. If yes, call next(). If no, res.send("Too young"). Apply this middleware *only* to a route named /vip. (Test it using URLs like /vip?age=15 and /vip?age=20).
Challenge 2: Install the NPM package morgan. Read its documentation, and replace your custom requestLogger with Morgan's app.use(morgan('dev')) middleware.
---
13. MCQs with Answers
Q1: What is the primary purpose of the next() function in middleware?
A) To send a response to the client.
B) To pass control to the next middleware or route handler in the pipeline.
C) To restart the server.
D) To skip the current request.
Answer: B
Q2: If a middleware function does NOT call next() and does NOT send a response, what happens?
A) The server crashes.
B) Node.js automatically calls next().
C) The client's request hangs and times out.
D) Express sends a 404 error.
Answer: C
Q3: How do you apply a middleware function globally to all routes?
A) app.all(middleware)
B) app.global(middleware)
C) app.use(middleware)
D) express.middleware(middleware)
Answer: C
Q4: Can a middleware function modify the req object before it reaches the route handler?
A) Yes
B) No
Answer: A
---
14. Interview Questions
- 1. What is Express Middleware?
and res objects, and a next function. They are used to execute code, modify request/response objects, handle errors, or terminate the request early (like for authentication).
-
2.
Why is the order of
app.use() important?
*Answer:* Express executes middleware in the exact order they are defined in the file. If a route handler (like app.get) matches the request and sends a response before a middleware is defined, that middleware will never execute.
---
15. FAQs
Q: Are route handlers (like
app.get('/', (req, res) => {})) considered middleware?
A: Yes! Technically, route handlers are just middleware functions that are restricted to a specific HTTP method and path, and they usually end the cycle by sending a response rather than calling next().
Q: How many middleware functions can I chain together?
A: As many as you want! You can pass an array of middlewares to a route:
app.get('/route', [mid1, mid2, mid3], handler);.
---
16. Summary
-
Middleware intercepts requests in the Express pipeline.
-
It has access to
req, res, and next.
-
You must call
next() to continue the pipeline, or end it with a response.
-
Use
app.use(middleware)` to apply it globally.
- Global middleware must be placed at the top of your file.
---
17. Next Chapter Recommendation
Now that we understand routing and middleware, let's talk about the frontend. How do we serve CSS, JavaScript, and images to the browser? In Chapter 16: Serving Static Files in Express, we will use a built-in middleware to easily host an entire static portfolio website!