Middleware in Express

Adam Zerner
3 min readMay 31, 2015

--

When I was learning Express, the idea of middleware always confused me. It still sorta does. So I’m going to write an article about it.

  1. As a way to further my own understanding.
  2. To help others.

Big Picture

First off, here’s the big picture:

In this scenario, the request starts off in the browser and ends up at C. A and B are middleware. They transform the request.

  • A transforms the request from POST / {num: 1} to POST / {num: 2} .
  • B transforms the request from POST / {num: 2} to POST / {num: 3} .

In this context, middleware is just some code that transforms the incoming request.

Express

To understand how middleware works in Express, there’s a few things you have to understand:

  1. You could have multiple routes that match an incoming request.
  2. Routes are executed from top to bottom.
  3. If a route matches an incoming request, subsequent routes that match the incoming request won’t be hit if you don’t call next().

Let me explain.

app.get('/', function(req, res) {
console.log('one');
});
app.get('/', function(req, res) {
console.log('two');
});

Here’s what will happen if there’s an incoming GET / request:

  • It will match the first route.
  • The code in the first route will be executed.
  • “The buck stops here”. Even though the second route matches the incoming request.

So how would we get “two” to be logged as well?

app.get('/', function(req, res, next) {
console.log('one');
next();
});
app.get('/', function(req, res) {
console.log('two');
});

Now…

GET /

outputs

one
two

Calling next() allows “the buck to not stop here”.

Consider this:

app.get('/', function(req, res, next) {
console.log('one');
next();
});
app.get('/foo', function(req, res) {
console.log('two');
});

What do you think would be the output after a GET / request?

Answer:

one

“two” wouldn’t be logged because the second route doesn’t match the request. next() just triggers the next matching route to run its callback.

Consider a different scenario:

app.get('/', function(req, res, next) {
console.log('one');
next();
});
app.get('/', function(req, res) {
console.log('two');
});
app.get('/', function(req, res) {
console.log('three');
});

What do you think the output will be?

Answer:

one
two

Why not one, two, three? Because next() wasn’t called in the second route’s callback. next() only “passes the buck” to the next matching route. Not to all matching routes.

So far we didn’t actually manipulate the request. Let’s see how to do that.

app.get('/', function(req, res, next) {
req.foo = 'bar';
next();
});
app.get('/', function(req, res) {
console.log(req.foo);
});
- - -GET /bar

The key point is that the request is an object, and it could be manipulated.

You could also pass multiple callbacks to app.VERB(). Like this:

app.get(
'/',
function(req, res, next) {
console.log('one');
next();
},
function(req, res, next) {
console.log('two');
next();
},
function(req, res) {
console.log('three');
}
);
- - -GET /one
two
three

app.use()

It works like this:

app.use('/', cb); 
/*
"matches everything that starts with /"
matches /
matches /foo
matches /foo/bar
*/
app.get('/', cb);
/*
"only matches /"
matches /
doesn't match /foo
doesn't match /foo/bar
*/

So app.use() could be used as middleware.

app.use('/', function(req, res, next) {
console.log('one');
next();
});
app.get('/', function(req, res) {
console.log('two');
});
- - -GET /one
two

If the callback is passed as the first argument to app.use(), it matches all routes.

app.use(function(req, res, next) {
console.log('one');
});
app.get('/sdljfsdjlf/sdfsj/dsfsdfs', function(req, res) {
console.log('two');
});
- - -GET /sdljfsdjlf/sdfsj/dsfsdfsone
two

But remember that order matters.

app.get('/', function(req, res) {
console.log('two');
});
app.use(function(req, res, next) {
console.log('one');
next();
});
- - -GET /two

Also check out these two Stack Overflow questions:

  1. What’s the difference between app.use() and app.get()?
  2. What’s the difference between app.all(‘*’) and app.use(‘/’)?

app.Router()

See here and here.

--

--

Adam Zerner

Rationality, effective altruism, startups, learning, writing, basketball, Curb Your Enthusiasm