DEV Community

Discussion on: Explain Middleware Like I'm Five

Collapse
 
isaacdlyman profile image
Isaac Lyman

Think of an HTTP request like an assembly line. The user is at the start of the line and submits the request. The server is the next person in line: it receives the request, attaches it to a (currently empty) response object, routes it to the correct endpoint, and passes it on. Then each other person in the line can alter the request and response (or not), then choose whether to pass them on, throw them away, or pick them up and throw them back to the user. Each of these other people is a middleware. At the end of the line is a person who does what the user actually asked for, whether that's fetching data, updating a database, or communicating with a third-party service. The user hopes the request will make it all the way to the end.

In Express (a popular NodeJS server framework), a middleware is a plain old JavaScript function. It does something to a request or a response, then either continues or terminates the request. For example, you might have an endpoint that looks like this:

app.get('/api/chapters', isAuthenticated, (req, res, next) => {
  getChaptersFromDatabase(req.user).then(chapters => res.send(chapters));
});
Enter fullscreen mode Exit fullscreen mode

So when the server receives an HTTP GET request at the /api/chapters URL, the first thing it will do is call the isAuthenticated function, which could look like this:

function isAuthenticated(req, res, next) {
  if (req.user.authenticationToken) {
    return next();
  } else {
    res.redirect('/auth');
  }
}
Enter fullscreen mode Exit fullscreen mode

This is a very small middleware. It has access to the request object req, the response object res, and a function next that can continue the request. We check whether the user has an authentication token (in a real-life app we would validate it somehow) and if so we continue the request, which in this case will proceed to get the user's chapters from the database and return them in the response. But if the user doesn't have a token, we redirect the user to the /auth page so they can log in.

The nice thing about a middleware is that it's separate from the logic of any other middlewares or the endpoint itself. We can use the same isAuthenticated middleware on any endpoint, just by adding it to the arguments of app.get(), app.post() or any other endpoint method.

We can use as many middlewares as we want on any endpoint:

app.get('/api/books', isAuthenticated, isPremium, validateRequest, transformRequest, (req, res, next) => {
  // Get the user's books and return them
});
Enter fullscreen mode Exit fullscreen mode

They're executed in order. When a GET request comes to this endpoint, Express will hand the request and response off to isAuthenticated, then isPremium, then validateRequest, then transformRequest, then the final arrow function. Any of these middlewares could choose to end the request (or make it time out by never calling next()), in which case everything will stop there--the other middlewares and the last function will never be called. But if they all choose to continue the request, the user will most likely get what they want.

You'll notice that the last argument in the list--the arrow function--looks a lot like a middleware. It pretty much is! The difference is that I didn't make it a named function, since it probably isn't shared with any other endpoint. Since it's just another middleware, we could put more middlewares after it, and if it called next(), they would get called too. But in my experience a function like this usually calls res.send(books) and the request ends there.

Middlewares in .NET work similarly, although they're a bit harder to set up. I assume the concept is the same in every server-side language.

Collapse
 
missixs profile image
missixs

thanks a lot, it was exactly what i wanted to know.