Time to Celebrate

Express is really popular. Any “getting started with Node” almost always includes a step where you install Express and go from there. It has become the defacto HTTP framework for node. Express is a fine framework and it really doesn’t need me to sing it’s praises.

Coming from the hapi world, one thing I really like about hapi is all the built in plumbing for validation. You can easily validate incoming request headers, route parameters, query string values, and the incoming request body. You can use your own functions to do this, but most people use joi.

If you haven’t heard of it, joi is a super powerful JavaScript validation library. You define a joi schema and validate it against a source object. The schema definition vocabulary is extremely expressive. You can set up default values, logical ORs and XORs, type definitions, regular expression checks, references to other values in the object… the list of what you can do with joi is well outside the scope of this post, but you owe it to yourself to check out joi. It’s not just for hapi; you can use it on any node project.


While I was working on my recent talk comparing Express and hapi, I wanted to demonstrate using joi to validate a request payload. Just a basic joi schema to make “name” required; “age” required and and integer; and “title” an optional string.

As I said, hapi has validation support built right into the framework, but Express does not. Like many things with Express, you’re left to your own devices and userland to solve these kinds of problems. So I started poking around looking for a joi validation middleware for Express. I assumed someone had already built one and I could just install it from npm and move on with my hapi vs Express comparison code.

My assumption was partially correct. There are indeed several joi middleware modules for Express. Unfortunately, they all have at least one of the following issues.

  1. They were using super old versions of joi
  2. They didn’t short-circuit the request and immediatly respond with an err if the validation failed
  3. They didn’t persist any changes joi made back to the incoming request object

In short, the existing joi middleware modules didn’t emulate the way hapi manages request validation. So since I’m pretty familiar with joi, hapi, and Express; I thought I’d make one more joi middleware module and hope it becomes the “go to” module for using joi with Express.

So with that long lead in — I’m happy to introduce celebrate.

celebrate logo a joi validation middleware for express

celebrate addresses all of the issues I had with the existing joi middleware modules I was able to find. It uses the latest published version of joi and I have every intention to keep it up to date. Since I’m part of the hapijs GitHub organization, I see every change and release of joi, so staying up to date with the major releases should be pretty easy.

I designed celebrate to emulate the hapi approach of responding with an error in the first instance of a validation error. The different parts of the incoming request are validated in the follow order:

  1. Request headers (req.headers)
  2. Request route parameters (req.params)
  3. Request query string (req.query)
  4. Request payload (req.body)

If any of those checks fail, that is considered an error and next is called with the validation error from joi. I thought this was important because I don’t know about you, but I don’t want to even start processing business logic unless I know I’m starting with valid input data. celebrate will only validate the different parts of a request you tell it to. You can use it to just check request query strings and nothing else or all four parts of a request; and everything else in between.

Finally, joi has the capacity to set default values and coerce types when configured to do so. Some of the other modules out there did not take this into consideration and did not apply these changes back to the validated value.


Examples

Alright, let’s see some examples of how you could use this

const express = require(‘express’);  
const BodyParser = require(‘body-parser’);  
const Joi = require(‘joi’);  
const Celebrate = require(‘celebrate’);

const app = express();  
app.use(BodyParser.json());

app.post(‘/signup’, Celebrate({  
 body: {
   name: Joi.string().required(),
   age: Joi.number().integer(),
   role: Joi.string().default(‘admin’)
 },
 query: {
   token: Joi.string().token().required()
 }
}), (req, res) => { res.send(req.body); });

app.use((err, req, res, next) => { res.status(400).send(err); });  

In this first example, we are validating the request body and the query string. If you try POSTing an empty body to “/signup”, you’ll get an error because you need to have a token. celebrate didn’t even try to validate the body yet because your query string isn’t valid per the joi schema.

Try posting to “/signup?token=1234abc”. Now you’ll start to see errors about missing “name”. As you can see, the query string is now valid, but the POST body is not. Try POSTing a name and an age. Once you have a valid payload, the server will respond with the request body as JSON. You’ll notice that if you don’t supply a “role”, “admin” will be applied to req.body. If you do specify a role, the original POSTed value will be used.


Let’s look at another example where you can use celebrate on all incoming requests. Suppose you have an API that needs a specific header to be set; if it’s not set, respond with an error message.

const express = require(‘express’);  
const Joi = require(‘joi’);  
const Celebrate = require(‘celebrate’);

const app = express();

app.use(Celebrate({  
 headers: Joi.object({
   token: Joi.string().required().regex(/abc\d{3}/)
 }).unknown()
}));
app.get(‘/’, (req, res) => { res.send(‘hello world’); });  
app.get(‘/foo’, (req, res) => { res.send(‘a foo request’); });  
app.use((err, req, res, next) => { res.status(400).send(err); });  

The joi schema above is validating req.headers on every request. The request headers must contain a “token” value and it must match the supplied regular expression. unknown() stipulates that other values are allowed in this object. We do this so things like “accept”, “host”, “user-agent”, and all those other browser-set HTTP headers are allowed by joi.

If you create a new server using the code above, you should notice that any requests will respond with an error. The message will indicate that your either missing “token” or it doesn’t match the correct pattern. You will get the same invalid responses from both “/” and “/foo”. Try including a “token” header with the value “abc123” and then make a request to “/”. You should get “hello world” back as a response.


As you can see from the two above examples, celebrate gives you a lot of validation options! You can validate on single routes or on every request. You can validate four different parts of the incoming request object and have the full suite of joi schema rules at your disposal. I hope you’ll give it a try on your next (or current) Express project. Contributions are always welcomed on the project GitHub page.