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.
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"
}
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'});
}
}
});
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'});
}
}
});
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"
}
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