Menu Close

Guide to the Express Application Object — Middleware

The core part of an Express app is the Application object. It’s the application itself.

In this article, we’ll look at the methods of the app object and what we can do with it, including using app.use to set middleware.

app.use([path,] callback [, callback…])

We can use the app.use method to mount middleware that’s run app-wide.

They can be run when requests for specific paths are made or for all paths.

It takes the following arguments:

  • path — it can be a string or regex representing paths or patterns of paths. The default is / .
  • callback — a function to handle requests. It can be a middleware function, a series of them, array of them, or a combination of all of the above

We can provide multiple callbacks that behave just like middleware, but they call next('route') to skip the remaining middleware functions.

We can use these to impose preconditions on some routes and pass control to the route if we don’t need to call the remaining middleware.

Since router and app implement the middleware interface, we can use them like any other middleware function.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
app.use((req, res, next) => {  
  console.log(`Request made at ${Date.now()}`)  
  next();  
})

app.get('/', (req, res) => {  
  res.send('hi');  
})

app.listen(3000);

Then we get the request time logged with every request.

We call next to call the route handler.

Middlewares are run sequentially, so the order they’re included in the code is important.

For example, if we have:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
app.use((req, res, next) => {  
  res.send('End with middleware');  
})

app.get('/', (req, res) => {  
  res.send('hi');  
})

app.listen(3000);

Then we get End with middleware instead of hi since we sent our response with the middleware, which we included first.

Error-Handling Middleware

We can create our own middleware for handling errors instead of using Express’s default error handler.

The error handler takes 4 arguments, which are the error object, request object, response object, and next function.

For instance, we can define one as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res, next) => {  
  try {  
    throw new Error('foo');  
  }  
  catch (ex) {  
    next(ex);  
  }  
})

app.use((err, req, res, next) => {  
  res.status(500).send('error');  
})

app.listen(3000);

The error should be displayed since we included the error handler after our GET request handler.

Paths

We can path in a string or regex path or path pattern as the first argument of the use method. This way, the middleware will only be called when the route matches the one specified in string or regex.

To match a constant path, we can write the following:

app.use('/abc', (err, req, res, next) => {  
  res.status(500).send('error');  
})

We can match patterns as follows. A ? will make the character preceding it optional:

app.use('/abc?d', (err, req, res, next) => {  
  res.status(500).send('error');  
})

The when requests made to /abcd or /abd has an error, the middleware above will be called.

The + sign means that one or more characters preceding it will be matched. For example, if we have:

app.use('/abc+d', (err, req, res, next) => {  
  res.status(500).send('error');  
})

The when requests made to /abccd,/abcccd, etc. has an error, the middleware above will be called.

* is a wildcard character. We can use it as follows:

app.use('/ab*cd', (err, req, res, next) => {  
  res.status(500).send('error');  
})

Then we get error when we make a request to /abcccd , /abFoocd , and so on if that has an error.

To match a group of optional characters, we can use parentheses and a question mark after it.

For instance, if we have:

app.use('/a(bcd)?e', (err, req, res, next) => {  
  res.status(500).send('error');  
})

Then we get error when we make a request to /abcde or/ae that has an error.

Passing in Middleware

We can pass in various combinations of middleware in addition to just one middleware.

We can pass in a router object as a middleware as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));const router = express.Router();  
router.get('/', (req, res, next) => {  
  res.send();  
});

app.use(router);app.listen(3000);

An Express app is also valid middleware:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
const subApp = express();
subApp.get('/', (req, res, next) => {  
  res.send();  
});

app.use(subApp);app.listen(3000);

We can also pass in a series of middleware as arguments:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
const r1 = express.Router();  
r1.get('/', (req, res, next) => {  
  console.log('r1 called');  
  next();  
});

const r2 = express.Router();  
r2.get('/', (req, res, next) => {  
  console.log('r2 called');  
  next();  
});  
app.use(r1, r2);
app.listen(3000);

Then they’ll be called in order, so we get:

r1 called  
r2 called

in the console.

Likewise, we can pass in an array of middlewares as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
const r1 = express.Router();  
r1.get('/', (req, res, next) => {  
  console.log('r1 called');  
  next();  
});

const r2 = express.Router();  
r2.get('/', (req, res, next) => {  
  console.log('r2 called');  
  next();  
});  
app.use([r1, r2]);
app.listen(3000);

Then we get the same output since they’re called in the same order that they’re listed.

A combination of them also works:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
const foo = (req, res, next) => {  
  console.log('foo called');  
  next();  
}

const r1 = express.Router();  
r1.get('/', (req, res, next) => {  
  console.log('r1 called');  
  next();  
});

const r2 = express.Router();  
r2.get('/', (req, res, next) => {  
  console.log('r2 called');  
  next();  
});

const subApp = express();  
subApp.get('/', (req, res, next) => {  
  console.log('subApp called');  
  next();  
})

app.use(foo, [r1, r2], subApp);app.listen(3000);

Then we get:

foo called  
r1 called  
r2 called  
subApp called

from the console. They’re still called in the order they’re listed.

Static Files

We can expose static folders with the express.static middleware. For example, if we want to expose the public folder in the project folder, we can write:

app.use(express.static(path.join(__dirname, 'public')));

Conclusion

app.use can be used to one more middleware in various combinations, including a single middleware, an array of middlewares, a comma-separated list of middlewares or a combination of them.

They’re called in the order that they’re included, and the next one can be called by calling next .

Error handlers are also middleware functions. The only difference is that they have an error parameter before the request and response parameters.

Finally, we can expose static folders with the express.static middleware.

Posted in Express, expressjs