Jan 22, 2023
A Beginner's Guide to Express Middlewares
Since Express is, by design, very minimalistic in terms of functionalities, the framework offers the possibility for developers to intercept an incoming HTTP request before it reaches the endpoint’s controller using a special type of function called a middleware.
These middlewares can be used for a variety of actions such as logging a request, verifying its headers, or parsing its payload, and can be chained one after the other in order to form what’s called a middleware stack.
The Request-Response Lifecycle
Just like any other request listener, a middleware function has access to the request and response objects, as well as an additional parameter named next.
function middleware(req, res, next) {
//
}
It can terminate the request-response lifecycle by sending an immediate response to the client using one of the methods of the response object, such as send()
, sendStatus()
, and so on.
For example:
function middleware(req, res, next) {
// Execute internal logic
res.sendStatus(200);
}
It can forward the request to the next middleware (or controller) in the stack, by invoking its next()
argument — as without it, the request will be left hanging until it times out.
For example:
function middleware(req, res, next) {
// Execute internal logic
next();
}
Note that any modifications made to the request and response objects — like the addition of a new property — will be preserved, and passed on to each component of the middleware stack.
Connecting Middlewares
In Express, a middleware function can be declared in two different ways:
- At the application-level.
- At the route-level.
Application-level middleware
An application-level middleware is a function that is bound to the application object through its use()
method:
app.use([PATH,] CALLBACK [, CALLBACK...])
Where:
PATH
represents an optional mount path, a path pattern, a regular expression or an array of combinations of these three.CALLBACK
represents a single middleware function or a series of middleware functions.
In this example, every single request sent to the application will first pass through the logger()
middleware function before being routed to the appropriate request listener.
const express = require('express');
const app = express();
const logger = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
};
const controller = (req, res) => res.sendStatus(200);
app.use(logger);
app.get('/foo', controller);
app.get('/bar', controller);
app.listen(8080);
You can try it yourself by running the following commands:
$ curl 127.0.0.1:8080/foo
GET /foo
$ curl 127.0.0.1:8080/bar
GET /bar
Route-level middleware
A route-level middleware is a function that is bound to a specified route by passing it as an argument of this route, between the path string and the controller.
app.METHOD(PATH, MIDDLEWARE[, MIDDLEWARE], HANDLER);
Where:
METHOD
is a function representing an HTTP method.PATH
is a string of characters representing a URN.MIDDLEWARE
is a middleware function.HANDLER
is a request listener function.
In this example, only the requests sent to the GET /foo
endpoint will first pass through the logger()
middleware function before being routed.
const express = require('express');
const app = express();
const logger = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
};
const controller = (req, res) => {
res.sendStatus(200);
};
app.get('/foo', logger, controller);
app.get('/bar', controller);
app.listen(3000);
You can try it yourself by running the following commands:
$ curl 127.0.0.1:8080/foo
GET /foo
$ curl 127.0.0.1:8080/bar