Using Joi plugin for validation in Hapi


boom hapi nodejs Tutorials validation

In Hapi, you can validate incoming data using the Joi plugin. It’s object schema language and provides a perfect mechanism to validate JavaScript objects. This plugin comes from Hapi infrastructure, so you don’t need to install any additional packages. You can create your own validation schemas, multiple and conditional schemas.

Payload validation

For example, you have the next form on client:

<form action="/form" method="post">
  <div>
    <label for="name">Name:</label>
    <input id="name" type="text" name="name" required/>
  </div>
  <div>
    <label for="mail">E-mail:</label>
    <input id="mail" type="email" name="mail" required/>
  </div>
  <div>
    <label for="msg">Message:</label>
    <textarea id="msg" name="msg"></textarea>
  </div>
</form>

The POST /form will receive the name, mail, msg fields. We can assume that the server should validate the fields according to the following logic:

  • name - a string, minimum 3 characters, maximum 15 characters, required;
  • mail - a string, real email, required;
  • msg - a string, can be empty.

Let’s create a basic Hapi server with POST /form route and include Joi:

'use strict';

const Hapi = require('hapi');
const Joi = require('joi');

const server = Hapi.server({
    port: 3000,
    host: 'localhost'
});

server.route({
    method: 'POST',
    path: '/form',
    options: {
        handler: async (req, h) => {
            return h.response({
                status: 'OK'
            });
        }
    }
});

const init = async () => {
    await server.start();
    console.log(`Server running at: ${server.info.uri}`);
};

process.on('unhandledRejection', (err) => {
    console.log(err);
    process.exit(1);
});

init();

You can define a validation schema in options.validate object inside the route for payload data. In our case, it will be the following:

 options: {
        validate: {
            payload: {
                name: Joi.string().min(3).max(12).required(),
                mail: Joi.string().email().required(),
                msg: Joi.string().optional()
            }
        },
        handler: async (req, h) => {
            return h.response({
                status: 'OK'
            });
        }
    }

If client will send the wrong data, server will response the following:

{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "Invalid request payload input"
}

Query validation

To validate query params, you need to pass your validation schema inside the options.validation.query property. For example, we send the size and page parameters to GET /orders endpoint. These parameters will be numeric and not negative. When the client don’t send them, server will set default values: size=20 and page=1. Our route will look like this:

server.route({
    method: 'GET',
    path: '/orders',
    options: {
        validate: {
            query: {
                page: Joi.number().min(0).default(1),
                size: Joi.number().min(0).max(100).default(20)
            }
        },
        handler: async (req, h) => {
            return h.response({status: 'OK'});
        }
    }
});

Params validation

If we want to validate the params in route, we should put validation schema to options.validation.params property. For example, if you have the route GET /orders/{id}, you should allow pass only numeric values to id parameter. An example:

server.route({
    method: 'GET',
    path: '/orders/{id}',
    options: {
        validate: {
            params: {
                id: Joi.number().required()
            }
        },
        handler: async (req, h) => {
            return h.response({status: 'OK'});
        }
    }
});

Headers validation

The validation of headers is possible using the options.validation.headers object. An example:

server.route({
    method: 'GET',
    path: '/orders',
    options: {
        validate: {
            headers: {
                'accept-encoding': Joi.number().min(0)
            }
        },
        handler: async (req, h) => {
            console.log(req);
            return h.response({
                status: 'OK'
            });
        }
    }
});

If headers will not pass validation, the client will get the following error:

{
    "statusCode":400,
    "error":"Bad Request",
    "message":"Invalid request headers input"
}

Schema methods

There are many methods to validate data using Joi. Dates can be validated using the following methods:

  • min()
  • max()
  • less()
  • greater()
  • timestamp()
  • iso()

Some of methods to validate strings:

  • creditCard()
  • email()
  • ip()
  • uri()
  • hex()
  • base64()

About all of these methods to validate numbers, strings, objects, boolean and etc, you can read in official Hapi doc https://github.com/hapijs/joi/blob/v14.3.1/API.md.

Learning Hapi.js? Buy my Hapi.js Handbook🔥

comments powered by Disqus