When we write backend code that interacts with end-users and takes and processes data from requests, we have to be super careful to put up some form of protection that filters out invalid data. For example, if we store the date of birth of a user in our database, then we cannot allow a value that is not a date to be stored, because it is not what we intend to be present and can lead to strange behavior.
To mitigate this, we can set up filters that analyze each and every data key sent by the frontend. We can do this all by ourselves without using a third-party library.
Assume in our code we want to check whether there is a key called parameterX which lies between 100 and 1000.
1
2
3
4
5
6
7
8
9
10
11
if (!data.parameterX) {
throw new Exception('parameterX missing')
}
try {
let value = parseInt(data.parameterX);
} catch (err) {
throw new Exception('parameterX should be number');
}
if (parseInt(data.parameterX) < 100 || parseInt(data.parameterX) > 1000) {
throw new Exception("Value out of range")
}
This is how to do it, and that is just for one key. What if we had ten more such keys, with varying complex requirements? Writing all these conditions by yourself is not good practice. Even if you somehow manage to write the conditions alone, without any error, it will make your code hard to read and debug. This is where Joi comes in. Its purpose is to make data validation easy to write and manage. You can read the documentation on Joi here.
Let’s start with installation. Set up an empty node project by running npm init in an empty folder and install express, Joi by running npm i express joi.
Now let’s have a quick look at how Joi works.
Assume you are working on an app that is supposed to be used by people at least sixteen years old. You are storing the name and age of users, so you would like to check the incoming data and make sure that both the name and age are present.
Create a file, index.js and paste following code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const Joi = require('joi');
const schema = Joi.object()
.keys({
name: Joi.string()
.min(3)
.max(40)
.required(),
age: Joi.number()
.integer()
.min(16)
})
const data = {
name: 'Srajan',
age: 10
};
const result = schema.validate(data);
console.log(result);
Run the file with node index.js, you will see the following result.
Let’s analyze our code.
First we create a schema, which describes what our data should look like. Our schema suggests that the data should be an object with the keys name and age. The name key will be a string with minimum length of three and maximum length of forty. The age key will be a number and integer (floating points not allowed), minimum value of sixteen. Both the name and age are required.
After that we call the validate function on the schema and send the data to validate.
We can even use regular expressions with Joi.
Let’s see how to integrate it in our API as a middleware.
Write the following code in index.js to set up a server.
1
2
3
4
5
6
7
8
9
10
const express = require("express");
const app = express();
app.use(express.json());
app.use(express.urlencoded({
extended: false
}));
app.listen(3000, () => {
console.log("Server Started");
});
Create a file called validationMiddleware.js. This will be used to check our data against various schemas. Paste the following code into it.
1
2
3
4
5
6
7
8
9
10
11
exports.validate = (schema) => (req, res, next) => {
const {
error
} = schema.validate(req.body);
if (error) {
res.status(422)
.send(error.details[0].message);
} else {
next();
}
};
It will receive a schema and check our req.body against the schema.
Now, create a file called validation.js. This will hold our schemas. Paste the following code into it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const Joi = require("joi");
exports.person = Joi.object()
.keys({
name: Joi.string()
.min(3)
.max(40)
.required(),
age: Joi.number()
.integer()
.min(16)
});
Now create an API like so, after importing both files.
const validation = require('./validation')
const {
validate
} = require('./validationMiddleware')
app.post("/", validate(validation.person), (req, res) => {
res.send("request processed");
});
Let’s test this now.
Make a post request without any body. You will get the following response.
Now, make a post request with a valid body. We get a successful response.
This is how you can use Joi to validate data.