markdown guide
 

Daniel is an expert fruit juicer. Fruit is being sent to him and he makes delicious juice out of them.

One day, he was sent a container full of apples. As he was about to start his work, he noticed that some had bad spots. Bummer, he thought, as he threw these bad apples away. Another shipment of oranges come in. He's surprised to see that some were too small for his liking. Annoyed, he threw them away as well. Then the last package came in, a bag of fresh mangoes. It turns out a handful were unripe! Feeling outraged, he threw those as well and continued his work.

Weeks went on Daniel had enough. Filtering through these fruits are taking too much time. To be fair, juicing is his skill, not filtering fruits. He decided to hire John, Jane, and Jonah. John checks the incoming shipment for bad spots, and hands off the good ones to Jane. Jane checks the sizes of the fruits and gives the one that have the right size to Jonah. Jonah, lastly, checks if the fruits are ripe for juicing. Alas, Daniel receives the fruits that are prime and ready for juicing!


Daniel = Server
Fruits = HTTP Request
John, Jane, Jonah = Middlewares


PS. Idk anything about juicing so this might be a silly example lmao.

 

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));
});

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');
  }
}

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
});

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.

 

Middlewares are awesome! The best way to show you is looking at this page in a popular PHP framework's docs: slimframework.com/docs/v3/concepts...

Basically, think about opening an onion and peeling back the layers til you get to the core. The core is your application and each layer is another service or layer of protection. What can you do with these layers of protection? Anything! You can check tokens, check that the values coming in are santizied, or strip them of whitespace, you can add headers to the HTTP response going out, say a Sunset header.

 
 

this is so much fun.
thanks for all the clarifying answers!
really helpful.

Classic DEV Post from Oct 8 '18

Who's looking for open source contributors? (October 8th edition)

It's one week into #hacktoberfest! Find something to work on or promote your p...

Daniel da Rocha profile image
fulltime architect coding during coffee breaks

Sore eyes?

dev.to now has dark mode.

Go to the "misc" section of your settings and select night theme ❤️