What is middleware in Express.js?
Middleware in Express.js refers to functions that have access to the request object (`req`), the response object (`res`), and the `next` middleware function in the application’s request-response cycle. These functions can perform various tasks, modify the request and response objects, terminate the request-response cycle, or pass control to the next middleware function.
What is Middleware?
At its core, an Express middleware function is a function that sits between the request coming into the server and the final route handler. It can execute any code, make changes to the request and the response objects, end the request-response cycle, and call the next middleware in the stack. The key to moving to the next middleware or route handler is calling next().
How Middleware Works
When an Express application receives a request, it passes through a series of middleware functions in the order they are defined. Each middleware function has the option to: 1) perform some operations, 2) modify the request or response, 3) call next() to pass control to the next middleware, or 4) send a response back to the client, thereby ending the request-response cycle. If a middleware function does not call next() or send a response, the request will hang.
Common Use Cases for Middleware
- Logging: Recording details about incoming requests (e.g., URL, method, timestamp).
- Authentication and Authorization: Verifying user credentials and permissions before granting access to routes.
- Parsing Request Bodies: Extracting data from
POSTandPUTrequests (e.g., JSON, URL-encoded data). - Session Management: Managing user sessions.
- Error Handling: Catching and processing errors that occur during the request-response cycle.
- Serving Static Files: Delivering static assets like images, CSS, and JavaScript files.
Types of Middleware
- Application-level middleware: Bound to an instance of the
appobject usingapp.use()orapp.METHOD(). It executes for every request to the application or specific routes. - Router-level middleware: Bound to an instance of
express.Router(). It functions exactly like application-level middleware but is isolated to a specific router instance. - Error-handling middleware: Always takes four arguments:
(err, req, res, next). This special signature tells Express to treat it as an error handler. It catches errors propagated by other middleware or route handlers. - Built-in middleware: Functions shipped with Express itself, such as
express.staticfor serving static files,express.jsonfor parsing JSON request bodies, andexpress.urlencodedfor parsing URL-encoded request bodies. - Third-party middleware: Middleware functions loaded using
require()for specific functionalities not built into Express, likebody-parser(though now largely replaced byexpress.jsonandexpress.urlencoded),corsfor Cross-Origin Resource Sharing, orhelmetfor setting security-related HTTP headers.
Example: Simple Logging Middleware
Here's an example of an application-level middleware function that logs the request method and URL for every incoming request. It then calls next() to pass control to the subsequent route handler or middleware.
const express = require('express');
const app = express();
// A custom middleware function to log requests
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next(); // Pass control to the next middleware function or route handler
});
// A route handler
app.get('/', (req, res) => {
res.send('Hello from Express with logging!');
});
// Another route handler
app.get('/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
In this example, every request to / or /users will first pass through the logging middleware, which prints a message to the console before next() hands over control to the respective route handler.