DEV Community

'Express Middleware' is just a fancy way of chaining a bunch of functions. Explained in 3 mins ๐Ÿ˜Ž

Let's start by creating the world's simplest Express server:

import express from "express";

const app = express();

app.get('/hello', (req, res) => {
  res.send('world'));
}
Enter fullscreen mode Exit fullscreen mode

It creates a route /hello that responds a body world.

Now let's change the last line just a little bit by adding a 3rd param called next:

app.get('/hello', (req, res, next) => {
  res.send('world'));
  next();
}
Enter fullscreen mode Exit fullscreen mode

Congratz! ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰ You've just created an Express Middleware. That simple!

Middleware == Function with next()

A middleware is just a function with three params (req, res, next), where next is a function that allows you to chain multiple functions. Let's see another example:


// returns true if server time is in AM
function isMorning() {...}

app.get("/hello",
  // middleware #1
  (req, res, next) => {
    if (isMorning()) {
      res.send("morning");
    } else {
      next();
    }
  },
  // middleware #2: called when isMorning() === false
  (req, res, next) => {
    res.send("afternoon");
  }
);
Enter fullscreen mode Exit fullscreen mode

Here we are chaining two middleware functions to handle /hello route. We use next() to pass control from the first middleware to the second.

In real-world scenarios, middlewares are useful for sharing common code among routes.

Example 1: Require user authentication for certain routes.

Here we created a middleware that only calls next() when user is authenticated. The middleware is shared by two routes. Note that when user is not authenticated, we don't call next(), which will stop the chain.

function RequireUserAuthentication(req, res, next) {
  if (req.user == null) {
    res.status("401").send("User is unauthenticated.");
  } else {
    next();
  }
}

app.get("/me/name", RequireUserAuthentication, (req, res, next) => {
  res.send(req.user.name);
});

app.get("/me/avatar", RequireUserAuthentication, (req, res, next) => {
  res.send(req.user.avatar);
});
Enter fullscreen mode Exit fullscreen mode

What if we want to use RequireUserAuthentication on all routes instead of specific routes?

Example 2: Require user authentication for all routes.

We can use app.use(RequireUserAuthentication) so the RequireUserAuthentication middleware is 'injected' into all routes.

One thing to note is that middlewares are ordered. In code below, any routes defined before app.use(RequireUserAuthentication) are not affected.

// This route is not affected by RequireUserAuthentication
app.get("/home", (req, res, next) => res.send(...));

// Require user auth for all routes defined after this line.
app.use(RequireUserAuthentication);

// Routes below are user sign-in required
app.get("/me/name", (req, res, next) => {
  res.send(req.user.name);
});

app.get("/me/avatar", (req, res, next) => {
  res.send(req.user.avatar);
});
Enter fullscreen mode Exit fullscreen mode

Example 3: Parse JSON request body into object.

Sometimes it's useful to automatically convert request body into a JSON object, so we don't have to write this same logic for every single route:

// Converts request body into req.body as a javascript object
function JSONParser(req, res, next) {
  if (req.headers['content-type'].startsWith('application/json')) {
    const rawBody = readStreamIntoString(req);
    req.body = JSON.parse(rawBody);
  }
  next();
}

// Apply JSONParser middleware to all routes defined after this line
app.use(JSONParser);

// Reads post name and content from req.body
app.get("/new/post", (req, res, next) => {
  const postTitle = req.body.title;
  const postContent = req.body.content;
  ...
});

// Reads username from req.body
app.get("/new/user", (req, res, next) => {
  const userName = req.body.username;
  ...
});
Enter fullscreen mode Exit fullscreen mode

Here we created a JSONParser middleware that parses JSON request body into an object, and sets the object as req.body. Later, the object is read from route /new/post and any other routes defined after.

Example 4: Configurable Middlewares

Let's go fancy here by creating a 'factory' function to return middleware functions. A function for functions ๐Ÿค–

function BodyParser(options) {
  if (options.type === "JSON") {
    return (req, res, next) => {
      if (req.headers["content-type"].startsWith("application/json")) {
        const rawBody = readStreamIntoString(req);
        req.body = JSON.parse(rawBody);
      }
      next();
    };
  } else if (options.type === "URL_ENCODED") {
    return (req, res, next) => {
      if (
        req.headers["content-type"].startsWith(
          "application/x-www-form-urlencoded"
        )
      ) {
        const rawBody = readStreamIntoString(req);
        req.body = new URLSearchParams(rawBody);
      }
      next();
    };
  }
}

app.use(BodyParser({ type: "JSON" }));
app.use(BodyParser({ type: "URL_ENCODED" }));
Enter fullscreen mode Exit fullscreen mode

In above code, we allow passing in an options param to control which middleware function to return.

In fact, this is exactly what bodyParser does for parsing request bodies (of course with more sophisticated code).

What are some of your middleware functions used in production? Leave a comment below to share your favorites โค๏ธโค๏ธโค๏ธ!

Check out getd.io and leave some feedback on what features you'd like to see next โค๏ธโค๏ธโค๏ธ!

Top comments (15)

Collapse
 
rafaacioly profile image
Rafael Acioly

Nice post! ๐Ÿ˜ƒ

Tip: you can add this article about Chain of responsability pattern so people that want to learn more about it can find easily

refactoring.guru/design-patterns/c...

Collapse
 
waqar903 profile image
waqar903

Nice Work..!
Can you please tell how can i upload my expressjs API's with mongoDB to live server with my domain hosting?

Collapse
 
techbos profile image
TechBos๐Ÿ˜Ž

Thank you for reading!

Where are you deploying your web server and mongoDB?

Collapse
 
waqar903 profile image
waqar903

for now i have it in my PC with localhost:3000
i am also curious about which server is needed for these Api's
lets suppose i have a linux base Hosting and i want to upload these api's there? is it possible? how?

Thread Thread
 
techbos profile image
TechBos๐Ÿ˜Ž

There are many ways you can deploy your server, and this comment is not the best place to answer your question. Probably the easiest approach is to copy your js files to the server and node run from there. For mongodb you can either start your own instance on the server, or find some free servers online which are great for dev purpose. Good luck!

Thread Thread
 
waqar903 profile image
waqar903

thanks for help.

Collapse
 
ddsilva profile image
Daniel Silva

Very nice post! Than you for sharing!

Maybe, there is a little bug in the functions returned by BodyParser, on last example. They aren't calling next() inside them. Is this correct? ๐Ÿค”

Collapse
 
techbos profile image
TechBos๐Ÿ˜Ž

Yep. Fixed. Good eye :D

Collapse
 
fsrezende profile image
Filipe S Rezende

Nice work! Contrate!!

Collapse
 
sebbdk profile image
Sebastian Vargr

offtopic, but why do people still use express when Koa is a thing? :D

Collapse
 
gregfletcher profile image
Greg Fletcher

Great post! Looking forward to more breakdowns!

Collapse
 
pcleddy profile image
Paul Charles Leddy

This. is. brilliant!

Collapse
 
shmdhussain12 profile image
Mohamed Hussain

Thanks for the simple and clear explanation on how the chaining works in express.

Collapse
 
jdg23dev profile image
Jamie

This is a treat of a post. Thank you โ˜บ๏ธ

Collapse
 
jangjuyeop profile image
JangJuYeop

Good Post!
I'll refer your post my TIL blogging!