DEV Community

Jacqueline Binya
Jacqueline Binya

Posted on • Edited on

Joi Tutorial-Using @hapi/joi version 16.1.7 to validate a request body in a RESTful API.

Intro

Why validate?

Before we even get started I know there is someone thinking, " Why should I bother with validations in the backend? Validations should be done in the front end, after all, we have inbuilt HTML5 form validations, why must I validate twice?
Well, it is good practice when designing your API to always design it in isolation never make any assumptions, cause, in all honesty, you never know who is going to consume your API.

So in a RESTful API, you typically have at least one HTTP POST method that accepts a payload of user data in json format. Now the question arises how then do we ensure that the data we receive is of the desired type and in the correct format before we persist that data in our application's database?

To do that we use middleware functions normally referred to as validators. The goal is to ensure that your application's validators cover all edge cases so as to protect the integrity of your database. And to do that you use either regular expressions or alternately handy modules like @hapi/joi which make input validations in Javascript easy, seamless and fast.

What then is @hapi/joi

From the official documentation from npmjs, @hapi/joi is defined as:"
The most powerful schema description language and data validator for JavaScript.
joi is part of the hapi ecosystem and was designed to work seamlessly with the hapi web framework and its other components (but works great on its own or with other frameworks)..."

Well to break it down, @hapi/joi is a module that is used to define a schema or blueprint of Javascript objects. Once the schema is defined, you then can use Joi's handy methods that come bundled with it, to validate any other objects against the schema. It was designed for the hapi ecosystem but works well with other frameworks of which for our purposes we will use it in an express server.

Getting Started

In your project set up a basic express server, and then install @hapi/joi by running the command npm i @hapi/joi on the terminal. This will install the current latest version of @hapi/joi of which at the time of publishing this article was version 16.1.7

In the root of your project create files:

  • schema.js
  • validators.js

In the file schema.js we will define our schema and in the file validators.js we will define our validator middleware functions.

A schema can be defined as either a Joi type or a simple Javascript object whose keys are joi types.

What are Joi types

Joi has inbuilt types e.g. Joi.object(), Joi.string(), Joi.array(), Joi.date() etc. More types are found listed in the official documentation.

Defining a schema

In practical applications, the schema is usually defined as a Joi object, whose keys have values which are Joi types and have optional constraints chained to them. Below are two ways I use to define a validation schema in schema.js

Method One

Alt Text

Method Two

Alt Text

The above schema definitions are equal whatever method you use totally depends on personal preference.

Validating a request body payload

Before we are able to perform any validations we should be able to communicate with our server, and to do that on app.js we add a route localhost:5000/signup as shown in the figure below:

Alt Text

When it comes to performing actual validations the Joi module provides various different methods we can use to validate our data as shown below:

Synchronous validations

Alt Text

Async validations

Alt Text

Validating schema by using Joi.assert()

Alt Text

When we run our server and send a payload via Postman as shown in the figure below using any of the above validators we get the same output. Joi by default aborts validations once the first rule is broken.
Alt Text

Alternately if you want to list all the validation errors in the payload, you can pass an option of { abortEarly: false }, to any of the above-listed Joi validator methods, this is usually handy for debugging purposes. For example:
Alt Text

If we start the server and on Postman send the same payload as above to the endpoint POST localhost:5000/signup, as a response we get a detailed error message:

{
    "error": {
        "_original": {
            "username": "",
            "email": ""
        },
        "details": [
            {
                "message": "\"username\" is not allowed to be empty",
                "path": [
                    "username"
                ],
                "type": "string.empty",
                "context": {
                    "label": "username",
                    "value": "",
                    "key": "username"
                }
            },
            {
                "message": "\"email\" is not allowed to be empty",
                "path": [
                    "email"
                ],
                "type": "string.empty",
                "context": {
                    "label": "email",
                    "value": "",
                    "key": "email"
                }
            }
        ]
    }
}

Custom Error Messages

So far we have been sending default Joi error messages in the response object which look like:

{
    "error": "\"username\" is not allowed to be empty"
}

The error message above is difficult to understand for the end-user. Error messages must be short and easy to understand. So to customize error messages on our schema definition in schema.js

Alt Text

As you can see above in the schema we modified the value of the username key and added an extra rule/constraints messages().
messages() takes an object as an argument, whose keys are validation error types and their corresponding values are custom error messages.

Now to view our customized error messages on the response object:

We start our server then on Postman, in the payload we post an empty string as a username. The response:

Alt Text

And then, we intentionally post an invalid type as a username to test the other custom error message, which in this case is a number. The response:

Alt Text

So our error messages have been successfully customized.

Validating strings

The Joi module provides several constraints we can use to increase validations on string data types which allow us to cover more edge cases. The most common ones I often use are in the example below:

Alt Text

In the example in the figure above:

  • string.trim() removes any whitespace before and after the username
  • string.min() specifies the minimum number of characters for username
  • string.max() specifies the maximum number of characters for username
  • string.regex() specifies a regular expression the username must match against.

Validating numbers

The important thing to note when validating numbers is to pass the option
{ convert: false } to your default Joi validator functions. It's especially effective when validating decimals.

Alt Text

In the example in the figure above:

  • number.min() specifies the minimum number for age
  • number.max() specifies the maximum number for age
  • number.positive() specifies that only positive numbers are accepted for the price
  • number.precision(limit) specifies the maximum permissible number of decimal places for the price.

Note The purpose of this article was to hopefully get you started on using the Joi modules for validations, it does not in any way cover everything about performing validations using the module, to learn more, I encourage you to go over the official documentation..

Thank you for reading my article, if you loved it please like it below, and follow me on Twitter. Increasing my Twitter following will help me land my dream job as a technical writer. If you have any comments and suggestions please let me know.

Top comments (6)

Collapse
 
anlixsigns profile image
André Litfin

Hi Jacqueline, thanks a lot for your post. If you want to have an even faster validation think of using joi.validate or joi.validateAsync instead of joi.assert: github.com/hapijs/joi/issues/2340

Collapse
 
jacqueline profile image
Jacqueline Binya • Edited

Thank you, I will sure check it out.

Collapse
 
mwafrika profile image
Mwafrika Josué

thanks so much Jacquie, this is the very meaningful post.

Collapse
 
makwasi1 profile image
Makwasi

Thanks for this post. Really great

Collapse
 
majiyd profile image
majiyd

Thank you for this post. It's better than the official documentation tbh.

Collapse
 
jacqueline profile image
Jacqueline Binya

Thanks that really means a lot to me.