JavaScript data validation with Joi

Jakub Dziworski
SoftwareMill Tech Blog
4 min readApr 16, 2018

--

Y u no validate?

Validating input to the system is one of the most trivial but at the same time the most neglected part of our job.
It is still however extremely important. With adequate input validation we can prevent:

  • Exposing security vulnerabilities like sql injection
  • Receiving incorrect type or format
  • Receiving too much data

It’s not too bad if the program implicitly throws error after receiving incorrect data. Most of the time we can track what went wrong in such cases.
However it becomes really scary when app treats malicious data as correct and proceeds it. Weird things can and will happen then.

Why then developers often neglect validation? In my opinion, because it’s easy, but at the same time relatively time-consuming and can lead to lots of boilerplate code filled with lots of conditional code.
This is especially true when using language such as JavaScript where checking type or existence of a value is often confusing.
I’ve seen and written such code many times — it’s definitely not a pleasant experience.

Schema validation with Joi to the rescue

However it doesn’t have to be that way. There are ways to validate data easily and enforce updating rules when the data format changes.

Ideally it would be great if we were able to define schema together with constraints for an object and validate it.
JavaScript wouldn’t be JavaScript if there weren’t multiple libraries offering such functionality:

  • Joi
  • Tcomb-validation
  • Validatorjs
  • Validate.js

They all are similar but in my opinion Joi is the best and most popular so we’ll focus on it.

Suppose we have an express app allowing to create, edit and view users.
There is a name and surname which shouldn’t be too short or too long.
There is also a year of birth which should be within some reasonable bounds.
Lastly, there is an email which should be valid email string. All of the fields except email are required. Here’s how we can validate such input using Joi:

Let’s test it:

Apart from validating, Joi gives us additional feature for free — it converts values to expected types. In the request body birthYear was sent as string, but according to schema it’s a integer. Joi was luckily able to validate it and convert it to number. As a result birthYear is a number in response body — that’s neat! Imagine how many if checks we’d have to write to accomplish the same task.

Another great feature about validating input using schema is that it forces developer to change it each time data format changes.
It is not true for manual conditional checks — they are often forgotten to be removed, while new ones are not created, leading to crippled validation.

Reusing schemas

Now let’s create endpoint for editing user. We’d like to allow to edit any attribute of user so all of the fields are optional:

It looks a lot like createUserSchema, but without required().
We all know code duplication is bad, but thanks to Joi schemas being immutable we can easily reuse them:

Edit user route might then look like this:

Validating requests with express middleware

So far we’ve been validating only request body. How about parameters, headers and query parameters?
It could get a little verbose using the same method we’ve seen so far — how about writing generic middleware, that can validate any request?
Let’s take a look at example of getting users, filtering optionally by surname.
To spice things up, our API will also require 20 character long token in a header for authorization:

Know your shortcuts

Joi also includes many shortcut functions shirking code significantly.

Default values — We could add role attribute to createUserSchema. If it is not provided it could be “customer” by default:

Referencing other fields in validated object — Suppose when creating admin, additional secret has to be included. Otherwise it’s forbidden to include it:

Arrays — Let’s include array of unique nicknames for user to our api, but allow up to maximum of 2:

Binary — Including base64 encoded optional user avatar image but not bigger than 2mb:

Dates — How about changing date of birth to exact day, but keeping min and max requirements:

Predefined string patterns (no more googling regexes) — Let’s add ip, credit card and homepage to our createUserSchema:

Custom error messages — In one of examples above we defined token schema (Joi.string().token().length(20).required()). If token is too short or too long, by default, Joi will create an error message token length must be 20 characters long. Sometimes we don’t want to expose such details for security reasons. To override this behavior use error function:

There are many, many more — I encourage you to take look at the api reference.

Summary

Validation in javascript is easy, quick and maintainable — there is simply no excuse to neglect it.
Just ditch your army of conditional checks and start using right tools designed exactly for this purpose.

--

--