DEV Community

Cover image for Three useful Express middleware
Zell Liew 🤗
Zell Liew 🤗

Posted on • Originally published at zellwk.com

Three useful Express middleware

As I created applications with Express and Node, I learned about three useful middlewares:

  1. Morgan
  2. Camelcase
  3. Remove empty properties

Of these three, Morgan is an actual middleware. You can download Morgan from npm directly. The other two are middlewares I created with camelcase-keys and omit-empty respectively.

I want to share what these three middlewares do, and how they make life easier for me when I'm creating applications.

Morgan4

Morgan is a request logger. It tells you several things when your server receives a request. It can log things like:

  • Date
  • HTTP version
  • Method
  • Referrer
  • Remote Address
  • Remote User
  • Request header
  • Response headers
  • Response time
  • Status code
  • Url of the request
  • User Agent

Morgan comes with five predefined formats for you to choose from:

  1. Combined
  2. Common
  3. Short
  4. Dev
  5. Tiny

I only use the dev format. A dev log from Morgan looks like this:

Example of a Dev log.

I use Morgan to check two things:

  1. The method and the endpoint
  2. Status codes

Checking the method and endpoint

When you write backend code, you need to make sure you send a request with the correct method and endpoint. If the method or endpoint is wrong, you will not be able to trigger the request handler you're expecting to trigger.

For example, if you want to trigger requestHandler in the code below, you need to send a GET request to the /testing endpoint.

app.get("/testing", requestHandler);
Enter fullscreen mode Exit fullscreen mode

If something goes wrong when I code backend applications, the first thing I check whether I'm sending the correct method and endpoint. Checking this first helps me save a ton of time debugging what would have been a typo.

When I send a request to the server, I get a log from Morgan. This log tells me the method and endpoint. The first value is the method. The second value is the endpoint.

Identifies the method and endpoint in a dev log.

Checking status codes

Since backend is about communication, I want to make sure I send the correct status code back to the frontend. If a user tries to login with an incorrect username or password, I want to send an 401 Unauthorized Error instead of a 500 Internal Server Error.

The best part about the dev format is it shows the status code with different colors. This makes status codes easier to spot.

A 200+ status code is green:

200 status code is green.

A 300+ status code is cyan:

300 status code is cyan.

A 400+ status code is yellow:

400 status code is yellow.

And a 500+ status code is red:

500 status code is red.

CamelCase

Let's say you want to get a user's first name from a form. To do this, you need a <form> in your HTML. The <form> should contain an <input> with the name of first-name.

<form>
  <input name="first-name" />
</form>
Enter fullscreen mode Exit fullscreen mode

To receive first-name in the backend, you need to use the bracket notation. This is because - is an operator in JavaScript. It is not recognized as a hyphen.

app.get("/endpoint", (req, res) => {
  // Bracket notation to get the value of a property
  const firstName = req.body["first-name"];
});
Enter fullscreen mode Exit fullscreen mode

I don't like using the bracket notation. I prefer using the dot notation whenever possible.

app.get("/endpoint", (req, res) => {
  // Dot notation
  const firstName = req.body.firstName;
});
Enter fullscreen mode Exit fullscreen mode

I prefer the dot notation because I use it everywhere. I'm used to writing camel case in JavaScript. It feels weird if I don't use the dot notation. Plus, I can destructure the property if I can use the dot notation.

app.get("/endpoint", (req, res) => {
  const { firstName } = req.body;
});
Enter fullscreen mode Exit fullscreen mode

To use dot notation, I need to make sure the name property in the <input> element is written in camel case.

<input name="firstName">
Enter fullscreen mode Exit fullscreen mode

But this feels weird, because we don't usually camel case stuff in HTML! We separate words with hyphens!

<!-- This feels weird -->
<input name="firstName" />

<!-- This feels normal -->
<input name="first-name" />
Enter fullscreen mode Exit fullscreen mode

My solution is to convert all properties into camel case when before it hits my request handler. I do this with a middleware I made using Sindre Sorhus's camelcase-keys package.

const camelcaseKeys = require("camelcase-keys");

const camelcase = () => {
  return function(req, res, next) {
    req.body = camelcaseKeys(req.body, { deep: true });
    req.params = camelcaseKeys(req.params);
    req.query = camelcaseKeys(req.query);
    next();
  };
};
Enter fullscreen mode Exit fullscreen mode

You can use the middleware like this:

app.use(camelcase());
Enter fullscreen mode Exit fullscreen mode

With camelcase, you don't have to worry about first name, first_name, first-name, or FirstName. It'll always be firstName.

It doesn't matter whether you're getting from req.body, req.params or req.query too. All properties will be in camel case.

Remove empty properties

Let's imagine a situation where you expect an array of skills.

fetch('/endpoint', {
  method: 'post',
  headers: { 'Content-Type': 'application/json' }
  body: JSON.stringify({
    name: 'Zell',
    skills: ['coding', 'designing', 'writing']
  })
}
Enter fullscreen mode Exit fullscreen mode

If there are one or more skills, you want to add the skills to the database.

app.post("/endpoint", (req, res) => {
  const { skills } = req.body;

  if (skills.length !== 0) {
    // Add skills to database
  }
});
Enter fullscreen mode Exit fullscreen mode

But we have a problem. Users can send you a variation of the request:

  1. Contains no skills property
  2. Contains an empty skills property
  3. Contains a skills property with at least one skill

If the user does not send you a skills property, you cannot write skills.length. You'll get an error that says Cannot read property 'length' of undefined.

To correctly check for one or more skills, you need two conditions:

  1. Check if there's a skills array
  2. Check if there's at least one item in the array
app.post("/endpoint", (req, res) => {
  const { skills } = req.body;

  if (skills && skills.length !== 0) {
    // Add skills to database
  }
});
Enter fullscreen mode Exit fullscreen mode

There's a way to simplify these checks. My solution is to create a middleware with Jon Schlinkert's omit-empty package.

omitEmpty removes empty properties from an object.

const object = {
  null: null,
  undefined: undefined,
  emptyString: "",
  emptyArray: [],
  emptyObject: {},
  filled: "yay"
};

console.log(omitEmpty(object));
// {
//   filled: 'yay'
// }
Enter fullscreen mode Exit fullscreen mode

Here's the middleware I made:

const omitEmpty = require("omitEmpty");

const removeEmptyProperties = () => {
  return function(req, res, next) {
    req.body = omitEmpty(req.body);
    req.params = omitEmpty(req.params);
    req.query = omitEmpty(req.query);
    next();
  };
};
Enter fullscreen mode Exit fullscreen mode

You can use removeEmptyProperties this way:

app.use(removeEmptyProperties());
Enter fullscreen mode Exit fullscreen mode

Once you use the removeEmptyProperties middleware, you don't have to check for the length of skills. You can be sure skills contains one or more items if it is present.

So the code becomes:

app.post("/endpoint", (req, res) => {
  const { skills } = req.body;

  if (skills) {
    // Add skills to database
  }
});
Enter fullscreen mode Exit fullscreen mode

Much simpler!


Thanks for reading. This article was originally posted on my blog. Sign up for my newsletter if you want more articles to help you become a better frontend developer.

Top comments (6)

Collapse
 
ajinspiro profile image
Arun Kumar

Hi, I don't understand why you create the high order function like this

const removeEmptyProperties = () => {
return function(req, res, next) {
req.body = omitEmpty(req.body);
req.params = omitEmpty(req.params);
req.query = omitEmpty(req.query);
next();
};
};

and use it like this

app.use(removeEmptyProperties());

Isn't creating the function like this

const removeEmptyProperties = (req, res, next) => {
req.body = omitEmpty(req.body);
req.params = omitEmpty(req.params);
req.query = omitEmpty(req.query);
next();
};

and using it like this

app.use(removeEmptyProperties);

enough?

Collapse
 
scriptmunkee profile image
Ken Simeon • Edited

Thanks for this write up. As a dev that's new to Node & Express, having a package like Morgan that provides me some details about the requests hitting my routes is fantastic. Plus it will reduces the amount of console.log() statements I'd have to write. 👍🏽

Collapse
 
eriknguyen profile image
Erik Nguyen

Hi, I'm wondering how is the impact of these to your app performance?

Collapse
 
bcdbuddy profile image
Babacar Cisse DIA

this is so much useful! thank you! 👍👍

Collapse
 
allanjeremy profile image
Allan N Jeremy

Perfect! Feels like I just discovered a hidden gem.

Downloading these as soon as I get back to a computer. Thanks for sharing Zell!

Collapse
 
prashanthr profile image
Prashanth R.

Super useful middleware(s) 💯