Middlewares in Express

sujesh thekkepatt
5 min readDec 10, 2019

--

Middlewares are one of the important concepts in the express. Express allows you to write functionalities that can be reused while creating routes for your express application. Middlewares are functions that have access to request, response objects. It may optionally contain the next function and an error object.

Creating application-level middleware

An express middleware is a function that has access to request, response object. So creating an express middleware is as simple as that defining/exporting a function with these two params,

module.exports = (req,res)=>{console.log(HEADERS: `${req.headers}`)
res.send("OK");
}

Now we created a simple middleware function. So as you can see we have complete control over the request-response cycle. You can modify the request object, send a response for the request by adding appropriate response headers, etc. So let’s create something interesting. We will create a middleware function that will act as a request logger. It will log the path of every incoming request. So open up your code editor and set up an express application. If you are not familiar with express.js get started here. For simplicity, I will create all my functions in index.js.

const app = require("express")()const PORT = 3000app.get('/',(req,res)=>{res.send("Hello World")
})
app.get("/hello",(req,res)=>{res.send("Hello");
});
app.listen(PORT,()=>{
console.log(`Listening @ ${PORT}`)
});

We wrote two endpoints “/hello” and “/”. As you can see we defined a request handler function of r both of them. And you guessed right, these are middleware functions. Every request handler is a middleware in the express.

And let’s create a middleware function in for logging our paths. So, modify your index.js to include the following code snippet,

function requestPathLogger(req,res) {console.log(`REQ PATH: ${req.path}`)
}

Now modify your request paths. So it will now look like,

app.get('/',requestPathLogger,(req,res)=>{res.send("Hello World")
})
app.get("/hello",requestPathLogger,(req,res)=>{res.send("Hello");
});

And now save and re-run your app. Now when you go to one of the defined paths you can the path is logged in your terminal as below,

Listening @3000
REQ PATH: /hello

But now we got a problem. Our request not getting processed. The browser still loading it. What might have caused this? Checking on the code we can find that the request is hitting the requestPathLogger but not to the response sending middleware. Since we do not send any response from requestPathLoggerthe request-response cycle never ends. So how can we overcome this?

To pass request to next available middleware function we use the “next” function

We can use the “next” function for this purpose. The “next” function allows you to call the next available middleware function for handling the request. So, modify the reqesutPathLogger function to use next as follows,

function requestPathLogger(req,res,next) {console.log(`REQ PATH: ${req.path}`);//call the next available middleware
next()
}

Now re-run the app and go to one of the defined paths. Now you can see that we get the desired output. We get the output from requestPathLogger and our server completes the request-response cycle.

Middleware can be path specific and can apply to all paths. That is we can have an authentication middleware setup for some path that needs to be authenticated to access and some path like loggers make available to all paths. Express app instance provides two types of methods for that,

  • app.use
  • app.METHOD

We have already used the app.METHOD for defining basic get handlers like app.get. etc. Let’s see how can we use app.use for setting up the requestPathLogger. Modify your index.js as follows,

app.use(requestPathLogger)app.get('/',(req,res)=>{res.send("Hello World")
})
app.get("/hello",(req,res)=>{res.send("Hello");
});

Now stop the server and re-run it. And go to any endpoint. Now we can see that we get the desired result. Now our code looks clean. We can also use the app.use function to a particular path. Say if we want our requestPathLogger to run only for the path“/hello” we can modify the code as follows,

app.use("/hello",requestPathLogger)app.get('/',(req,res)=>{res.send("Hello World")
})
app.get("/hello",(req,res)=>{res.send("Hello");
});

Now go to root path (“/”) we will get the output in the browser but no logging output can’t be seen. But when you goto “/hello” we can see both the output from logger and in the browser.

You can see that the output from logger is '/' for the path “/hello”. It’s because app.use is mouting the middlwares in the path “/hello”. This isolation is provided by express. So it consider “/hello” as the root.

Creating Error handling middleware

Error handling middleware is the middleware that has access to an additional error object as the first param. So the basic skeleton structure of the error-handling middleware function is as follows,

function errorLogger(err,req,res,next){// do something with err//call next error middleware using next()// or end the response with a customized error messageres.status(500).send('Internal Server error')
}

You must use all the four params while writing your error logger. Although you may not use next but it should be in your function signature. Otherwise it will be considered as regular middleware.

So let’s modify our code to include an authenticated endpoint. This endpoint only can be accessed using an access token in the request header (We are just faking this functionality now.)

function errorLogger(err,req,res,next){res.status(500).send(`Error occured -- ${err.message}`)}app.use("/hello",requestPathLogger)app.get('/',(req,res)=>{res.send("Hello World")
})
app.get("/hello",(req,res)=>{res.send("Hello");
});
app.get('/profile', (req, res, next) => {try {//validate access token presence.// we are faking it by throwing an errorthrow new Error("A valid access token required to access this resource");}catch (err) {next(err);}})app.use(errorLogger)

Now if we go to “/profile” we get an error message. We can chain multiple error handler the same way as we did for application middleware.

The order in which middleware registered plays an important role. So it’s better to resgister your error handling middleware at last.

There is also router level middleware. Which works the same as application-level middleware except it bound for the express router instance. We can also install any may thrid party middleware from the npm repository. There are thousands of middleware which provide a different kind of functionalities for your express application.

Things to consider,

  • Middleware registration has priorities. The first one registered to get access to first.
  • error middleware must have all the four parameters.
  • If a middleware ends the response cycle the next middleware never gets called.
  • Router level middleware same as application-level middleware except that it is bound to the router instance.
  • You can install numerous middleware packages from npm and use it with your app. eg: compression,body-parser, JSON, etc

Hey, I am Sujesh, a developer working on Nodejs. I occasionally write on Nodejs which I find useful. If you like my work and want to support it, buy me a cup of coffee!Thanks.

--

--