How Node JS middleware works?

Pankti Shah

Jan 17, 2020 | 6 min read

What is Express Middleware?

  • Middleware literally means anything you put in the middle of one layer of the software and another.
  • Express middleware are functions that execute during the lifecycle of a request to the Express server.
  • Each middleware has access to the HTTP request and response for each route it’s attached to.
  • Additionally, middleware can either terminate the HTTP request or pass it on to another middleware function using next. This “chaining” of middleware allows you to compartmentalize your code and create reusable middleware.

Requirements to Write Express Middleware

There are a few things you will need installed to create, use, and test Express middleware. First, you will need Node and NPM. To ensure you have them installed, you can run:

npm -v && node -v

You should see the Node and NPM versions you have installed. If you get an error, you need to install Node. All the examples should work with Node versions 8+ and NPM versions 5+.

I will also be using Express version 4.x. This is important because major changes were made from version 3.x to 4.x.

Express Middleware: The Basics

To get started, you’ll use the most basic of Express’ built-in middleware. Create a new project and npm init it…

npm init
npm install express --save

Create server.js and paste the following code:

const express = require('express');
const app = express();

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

app.listen(3000);

What problem does middleware solve? Why would I use it ?

You’re running a web application on a web server with Node.js and Express. In this application, let’s say your certain pages that require that you log in.

When the web server receives a request for data, Express gives you a request object with information about the user and the data they are requesting. Express also gives you access to a response object that you can modify before the web server responds to the user. These objects are usually shortened to req, res.

Middleware functions are the perfect place to modify the req and res objects with relevant information. For example, after a user has logged in, you could fetch their user details from a database, and then store those details in res.user.

What does a middleware function look like?

async function userMiddleware (req, res, next) {
    try {
        const userData = await getUserData(req.params.id);  //see app.get below

        if(userData) {
                req.user = userData;
                next();
        }
    } catch(error)  {
        res.status(500).send(error.message); //replace with proper error handling
    }
}

If there is an error and you don’t want any further code to be executed, just don’t call that function. Remember to send a response in that case, otherwise the client would be left waiting for a response until it times out.

var app = express();

//your normal route Handlers
app.get('/user/:id', userMiddleware, userController);

Middleware chaining

You can chain middlewares, either in the middlewares array or by using multiple app.use calls:

app.use(middlewareA);
app.use(middlewareB);
app.get('/', [middlewareC, middlewareD], handler);

Middleware Order is Important

When a request is received by Express, each middleware that matches the request is run in the order it is initialized until there is a terminating action.

So if an error occurs, all middleware that is meant to handle errors will be called in order until one of them does not call the next() function call.

Types of express middleware

  • Router level middleware eg: router.use
  • Built-in middleware eg: express.static,express.json,express.urlencoded
  • Error handling middleware eg: app.use(err,req,res,next)
  • Third party middleware eg: bodyparser,cookieparser

Router level middleware

  • express.Router Use the express.Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system.
  • You could use middlewares for logging, authentication, like the following to log the latest activity of a user and parses an authentication header and uses it to determine which user is currently logged in and adds it to the Request object.
  • The function is executed every time the app receives a request. if there is an error, it will just end the response and not call subsequent middlewares or the route Handler.
var router = express.Router()
Load router-level middleware by using the router.use() and router.METHOD() functions.
The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app.
var express = require(‘express’);
var router = express.Router();

// a middleware function with no mount path. This code is executed for every request to the router
// logging
async function logMiddleware (req, res, next) {
	try {
	     console.log(req.user.id, new Date());
     next();
    } catch() {
        res.status(500).send(error.message);
    }
}
// authentication
    async function checkAuthentication(req, res, next) => {
// check header or url parameters or post parameters for token
const token = req.body.token || req.query.token || req.headers['x-access-token'] || req.headers['authorization'];
  	if (token) {
        try {
            // verifies secret
            req.decoded = await jwt.verify(token, config.secret)

            let checkUser = await authenticateTokenHelper.getUserDetail(req);

            // if everything is good, save to request for use in other routes
                if (checkUser) {
                        req.user = req.decoded
                        next()
                } else {
                    return res.status(403).json({ 
                    message: responseMessage.noAuthorized 
                    })
                }
        } catch (err) {
            return res.status(401).json({ message: responseMessage.invalidToken })
        }
  } else {
    // if there is no token
    return res.status(400).json({ message: responseMessage.invalidRequest })
  }
}
router.use(logMiddleware);
	router.get('/user, checkAuthentication, handler);

Build-in middleware

Express has the following built-in middleware functions:

  • express.static serves static assets such as HTML files, images, and so on.
  • express.json parses incoming requests with JSON payloads.
  • express.urlencoded parses incoming requests with URL-encoded payloads.

Error handling middleware

Error-handling middleware always takes four arguments (err, req, res, next). You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors. The basic signature looks like this:

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

Example 1:

app.get('/users', (req, res, next) => {
  next(new Error('I am passing you an error!'));
});
app.use((err, req, res, next) => {
  console.log(err);    
  if(!res.headersSent){
    res.status(500).send(err.message);
  }
});

In this case, the error handling middleware at the end of the pipeline will handle the error. You might also notice that I checked the res.headersSent property. This just checks to see if the response has already sent the headers to the client. If it hasn’t it sends a 500 HTTP status and the error message to the client.

Example 2:

You can also chain error-handling middleware. This is common to handle different types of errors in different ways:

app.get('/users, (req, res, next) => {
  let err = new Error('I couldn\'t find it.');
  err.httpStatusCode = 404;
  next(err);
});

app.get('/user, (req, res, next) => {
  let err = new Error('I\'m sorry, you can\'t do that, Dave.');
  err.httpStatusCode = 304;
  next(err);
});

app.use((err, req, res, next) => {
   // handles not found errors
  if (err.httpStatusCode === 404) {
    res.status(400).render('NotFound');
  }
   // handles unauthorized errors 
  else if(err.httpStatusCode === 304){
    res.status(304).render('Unauthorized');
  }
    // catch all
   else if (!res.headersSent) {
     res.status(err.httpStatusCode || 500).render('UnknownError');
  }
  next(err);
});
  • In this case, the middleware checks to see if a 404 (not found) error was thrown. If so, it renders the ‘NotFound’ template page and then passes the error to the next item in the middleware.
  • The next middleware checks to see if a 304 (unauthorized) error was thrown. If it was, it renders the ‘Unauthorized’ page, and passes the error to the next middleware in the pipeline.
  • Finally the “catch all” error handler just logs the error and if no response has been sent, it sends the error’s httpStatusCode (or an HTTP 500 status if none is provided) and renders the ‘UnknownError’ template.

Third party level middleware

In some cases we will be adding some extra features to our backend Install the Node.js module for the required functionality, then load it in your app at the application level or at the router level.

Example: body-parser All middlewares will populate the req.body property with the parsed body when the Content-Type request header.

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())
app.post('/save',(req,res)=>{
	res.json({
		"status":true,
		 "payload":req.body
	})
}
app.listen(3000,(req,res)=>{
	console.log('server running on port')
})

In conclusion

Middleware functions are a really great way to run code on each request, or on each request for a certain route, and to take action on request or response data. Middleware is a crucial piece of any modern web server, and is incredibly useful.

· · · ·

Third Rock Techkno is a leading IT services company. We are a top-ranked web, voice and mobile app development company with over 10 years of experience. Client success forms the core of our value system.

We have expertise in the latest technologies including angular, react native, iOs, Android and more. Third Rock Techkno has developed smart, scalable and innovative solutions for clients across a host of industries.

Our team of dedicated developers combine their knowledge and skills to develop and deliver web and mobile apps that boost business and increase output for our clients.