Expressjs - Middlewares

expressjs

What is a middleware?

The middleware is a pass thru functions that add something useful to the request as it travels along each of them, for example req.body or req.cookie.

Middleware is a function with access to the request object (req), the response object (res), and the next middleware in line in the request-response cycle of an Express application, commonly denoted by a variable named next.

Middleware is basically a function which accepts request and response objects and a next function. Each middleware can decide to respond by using a response object or pass the flow to the next function by calling the next callback.

What can a middleware do?

Middleware can:

  • Execute any code.
  • Make changes to the request and the reponse objects.
  • End the request-response cycle.
  • Call the next middleware in the stack.

What must a middleware do if it does not terminate the request response cycle?

If the current middleware does not end the request-response cycle, it must call next() to pass control to the next middleware, otherwise the request will be left hanging.

How can a middleware terminate the request response cycle?

To terminate the request response cycle, do not invoke the next() method.

Can a middleware be loaded for just a specific path?

Yes. With an optional mount path, middleware can be loaded at the application level or at the router level. Also, a series of middleware functions can be loaded together, creating a sub-stack of middleware system at a mount point.

What are the 4 kinds of middleware?

  • Application-level middleware
  • Router-level middleware
  • Built-in middleware
  • Third-party middleware

What is an application level middleware?

Application level middleware are bound to an instance of express, using app.use() and app.VERB().

How can we configurate an application middleware so that it get executed for every request regardless of HTTP method or path?

// a middleware with no mount path; gets executed for every request to the app
var app = express();
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

How can we configure an application middleware so that it get executed for a certain path but regardless of HTTP method?

// a middleware mounted on /user/:id; will be executed for any type of HTTP request to /user/:id
var app = express();
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

The above middleware get executed regardless if the request was a GET or a POST.

How can we configure an application middleware such that it get executed for only a specific path and a specific HTTP method?

// a route and its handler function (middleware system) which handles GET requests to /user/:id
var app = express();
app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

The above middleware get executed only if the request path match /user/:id and the HTTP method is GET.

How can we load a series of middleware for a specific path?

// a middleware sub-stack which prints request info for any type of HTTP request to /user/:id
var app = express();
app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

How can a middleware pass control onto the next middleware?

By calling next():

next();

How can we load middleware for a specific path?

In version 4 you can now load middleware on a path with a variable parameter and read the parameter value from the route handler. For example:

app.use('/book/:id', function(req, res, next) {
  console.log('ID:', req.params.id);
  next();
})

The above middleware is only applicable to the specified path ('/book/:id')

Is there any differences between a middleware and a route?

I am not really sure on this. Route handlers, being a middleware system, makes it possible to define multiple routes for a path. In the example below, two routes are defined for GET requests to /user/:id. The second router will not cause any problems, however it will never get called, because the first route ends the request-response cycle.

// a middleware sub-stack which handles GET requests to /user/:id
app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('User Info');
});

// handler for /user/:id which prints the user id
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id);
});

If you need to skip the rest of the middleware from a router middleware stack, call next('route') to pass on the control to the next route. Note: next('route') will work only in middleware loaded using app.VERB() or router.VERB().

// a middleware sub-stack which handles GET requests to /user/:id
app.get('/user/:id', function (req, res, next) {
  // if user id is 0, skip to the next route
  if (req.params.id == 0) next('route');
  // else pass the control to the next middleware in this stack
  else next(); // 
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for /user/:id which renders a special page
app.get('/user/:id', function (req, res, next) {
  res.render('special');
});

What is the difference between next() and next('route')?

Route handlers, being a middleware system, makes it possible to define multiple routes for a path. next() will transfer control to the next middleware, but next('route') will transfer control to the next route, skipping the remaining middlewares that were configured for the current route / path.

What is router level middleware?

Router level middleware work just like application level middleware except they are bound to an instance of express.Router().

var router = express.Router();

How are router level middlewares loaded?

Router level middleware are loaded using router.use() and router.VERB().

How can we load a router level middleware so that it get executes for ever request to the router regardless of HTTP method or path?

// a middleware with no mount path, gets executed for every request to the router

var router = express.Router();
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

How can we load a router level middleware stack so that they get executed for certain route / path regardless of HTTP method?

// a middleware sub-stack shows request info for any type of HTTP request to /user/:id
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

How can we load a router level middleware stack so that they get executed only for a specific route / path and a specific HTTP method?

// a middleware sub-stack which handles GET requests to /user/:id
router.get('/user/:id', function (req, res, next) {
  // if user id is 0, skip to the next router
  if (req.params.id == 0) next('route');
  // else pass the control to the next middleware in this stack
  else next(); // 
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

How can we mount a router?

// mount the router on the app
var app = express();
var router = express.Router();

app.use('/', router);

Why does Express has the concept of router level middleware?

Having the concept of router level middleware allow us to use modules are developed by other developers. The author of each module can export a router object, and we just have to mount the router on whichever path we choose.

How can we implement middleware as module?

  • var foo = require('middleware') is called requiring or using a Node.js module. Then the statement var mw = foo() typically returns the middleware.
  • app.use(mw) is called adding the middleware to the global processing stack.
  • app.get('/foo', mw, function (req, res) { … }) is called adding the middleware to the "GET /foo" processing stack. The middleware mw sit between the path and your own function, so it is in the middle and therefore called "middleware". Your function is also a middleware. This is just like the middleware stack that we seen above.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License