Skip to main content

Using joi

What is joi?

joi (pronounced "joy") is a JavaScript library for validating and sanitizing input data. It is commonly used in the Node.js ecosystem, particularly with the Express.js web framework. It is a very powerful library that can be used to validate almost any kind of data. It is very easy to use and has a very simple syntax.

How to use joi?

I am dividing this section into three steps:

  1. Install joi
  2. Create a schema
  3. Use the schema to validate data

Install joi

Install the joi library:

npm install joi

Create a schema

To create a schema, we need to use the Joi.object() method. This method returns a schema object. We can use the keys() method of the schema object to define the keys of the object. We can use the Joi functions to define the validation rules for each key. We can use number() to mention type, use min(), and max() for obvious reasons, and use the required() method to make a key required. Check the following example:

Schema
const schema = Joi.object().keys({
parameterX: Joi.number().min(100).max(1000).required(),
// It will check if the parameterX's type is number, and length is between 100 to 1000 and required.
});

See, how easy it is to create a schema, and mention the validation rules for each key.

Use the schema to validate data

To validate data, we need to use the validate() method of the schema object. This method takes the data to be validated as an argument. It returns an object with two properties: error and value. If the data is valid, then the error property will be null. If the data is invalid, then the error property will contain the error message. Check the following example:

Validate data using schema
// Here I am using the same schema I made in step two
const { error } = schema.validate(data);
if (error) {
throw new Error(error.details[0].message);
}

Complete code

Above, I have explained the three steps to use joi to validate data. Now, I will show you a complete example of how to use joi to validate data in an Express app.

index.js
import express from "express";
import Joi from "joi";
const app = express();

app.post("/register", (req, res) => {
const schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(/^[a-zA-Z0-9]{3,30}$/),
repeat_password: Joi.ref("password"), // password and repeat_password must match
birth_year: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email(),
});

const { error } = schema.validate(req.body);
if (error) {
// If data is invalid, send 400 Bad Request response
return res.status(400).send(error.details[0].message);
}

// If data is valid, then
// Create user in the database, etc, and send the response
res.send("User created successfully");
});
// highlight-stop

app.listen(3000);

In this example, first I created schema, then. used the schema.validate() method of the schema object to validate the data. If the data is valid, then the error property will be null. If the data is invalid, then the error property will contain the error message. I am sending the error message as a response if the data is invalid.

Validation as middleware

To use joi validation as middleware in an Express app, you can create a middleware function that performs the validation and attach it to the routes that need it

middlewares/validation.js
const validate = (schema, key) => {
return (req, res, next) => {
const { error } = schema.validate(req[key]);
if (error) {
return res.status(400).send(error);
}
next();
};
};

export default validate;

The validate() function takes a joi schema as an argument and returns a middleware function that performs the validation. If the data is valid, the middleware calls next() to pass control to the next middleware function in the stack. If the data is invalid, the middleware sends a response with a status code of 400 (Bad Request) and an error message.

The middleware function can be used in a route handler like this:

routes/users.js
import express from "express";
import Joi from "joi";
import validate from "../middlewares/validation.js";

const router = express.Router();

const schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(/^[a-zA-Z0-9]{3,30}$/),
repeat_password: Joi.ref("password"),
birth_year: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email({ minDomainSegments: 2 }),
});

router.post("/", validate(schema, "body"), (req, res) => {
// create a user in the database, etc.
res.send("User created successfully");
});

// ...

Benefits of using it as middleware

Using joi validation as middleware has several benefits:

  • It allows you to centralize the validation logic in a single place, rather than repeating the same validation code in multiple routes or route handlers.
  • It makes it easier to reuse validation schemas across different routes and route handlers.
  • It separates the validation logic from the route handler logic, making the code easier to read and maintain.
  • It helps ensure that only valid data is passed to the route handler, reducing the risk of errors and bugs caused by invalid data.

So, each time we want to validate a request, we can use the schema.validate(data) function to create a middleware function that performs the validation. This middleware function can then be attached to the route handler(s) that need it. But we of course need to create a schema for each request. This is where joi shines. We can create a schema for each request and reuse it in multiple routes. This is a very powerful feature of JOI.

Validating query strings, and params

For now, we have only validated the request body. But we can also validate the query strings and params. To validate the query strings, we can use the query() method of the schema object. To validate the params, we can use the params() method of the schema object. Check the following example:

const schema = Joi.object().keys({
page: Joi.number().integer().min(1).max(100).required(),
limit: Joi.number().integer().min(1).max(100).required(),
sort: Joi.string().valid("asc", "desc"),
category: Joi.string().valid("books", "movies", "music"),
search: Joi.string().min(3).max(30),
});

const result = schema.validate(req.query);

if (result.error) {
return res.status(400).send(result.error);
}

// ...

Now, I am validating the params in the following way:

const schema = Joi.object().keys({
id: Joi.number().integer().min(1).required(),
});

const result = schema.validate(req.params);

if (result.error) {
return res.status(400).send(result.error);
}

// ...

These are just a few examples of how you can use joi to validate data in Express.js. You can use joi to validate any type of data, not just HTTP requests.

Code

You can download the related code from here

In this example, I have created a simple Express app that uses joi to validate data. I created middleware functions to validate the request body, query strings, and params in one go. I also created a schema for the body, query strings, and params. Check it out.

Conclusion

In this article, we learned how to use joi to validate data in Express.js. We also learned how to use joi validation as middleware in an Express app. We also learned how to create a schema for each request and reuse it in multiple routes.