Express Middleware

Understanding Middleware

Middleware functions are the backbone of an Express application's request-response cycle. They allow developers to process incoming requests, apply business logic, and prepare responses efficiently. Imagine middleware as a relay race where each runner (function) processes the baton (request and response objects) and passes it to the next runner.

Anatomy of a Middleware Function

A middleware function in Express is a function that takes three arguments in the following order:

If you think of the request as a package being delivered, middleware functions are the sorting centers that process and forward the package to its destination.

Middleware in Action

Let's create a middleware function logTime that logs the current time of a request:


const express = require('express');
const app = express();

const logTime = (req, res, next) => {
  console.log("Current time: ", new Date().toISOString());
  next();
};

app.get("/", logTime, (req, res) => {
  res.send("Hello World!");
});

app.listen(3000, () => console.log("Server running on port 3000."));

      

When a user accesses localhost:3000, the logTime middleware logs the current time before the server responds with "Hello World!".

Chaining Middleware

Middleware functions can pass data to each other. Here's an example:


const passOnMessage = (req, res, next) => {
  req.message = "Hello from middleware!";
  next();
};

app.get("/", logTime, passOnMessage, (req, res) => {
  console.log(req.message);
  res.send("Hello World!");
});

      

Think of this as a relay race where each runner not only passes the baton but also adds a message to it for the next runner to use.

Application-Level Middleware

To run middleware on every route, use app.use():


app.use(logTime);

app.get("/", (req, res) => res.send("Hello World!"));
app.get("/bye", (req, res) => res.send("Goodbye!"));

      

Now, logTime runs for every request, regardless of the route.

Key Takeaways

Middleware enables developers to build scalable and maintainable applications by compartmentalizing logic into reusable functions.

Real-World Applications