Menu Close

Server-Side Development with Hapi.js — Response Validation

Hapi.js is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Hapi.js.

Validate Responses

We can validate responses with Hapi.

For example, we can write:

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

const bookSchema = Joi.object({
  title: Joi.string().required(),
  author: Joi.string().required(),
  isbn: Joi.string().length(10),
  pageCount: Joi.number(),
  datePublished: Joi.date().iso()
});

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });
  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      return [
        {
          title: 'book',
          author: 'john smith',
          isbn: '1234567890',
          pageCount: 100,
          datePublished: '2020-02-01'
        }
      ];
    },
    options: {
      response: {
        schema: Joi.array().items(bookSchema),
        failAction: 'log'
      }
    }
  });
  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We create the bookSchema with the Joi.object method.

It lets validate the objects we have in our response.

We specify that we have the title , author , isbn , pageCount and datePublished fields.

Joi.string().required() makes the title and author field required.

isbn is set to have a string with length by writing:

Joi.string().length(10)

pageCount is number and datePublished is set to have a YYYY-MM-DD format.

We pass that into Joi.array().items() so that we specify that each entry of the array follows the schema.

failAction is 'log' so when validation fails, the failure is logged.

We enabled logging with:

debug: { request: ['error'] }

so we can see the error.

We can choose to only validate a percentage of the responses.

For instance, we can write:

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

const bookSchema = Joi.object({
  title: Joi.string().required(),
  author: Joi.string().required(),
  isbn: Joi.string().length(10),
  pageCount: Joi.number(),
  datePublished: Joi.date().iso()
});

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });
  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      return [
        {
          title: 'book',
          author: 'john smith',
          isbn: '1234567890',
          pagecount: 100,
          datePublished: '2020-02-01'
        }
      ];
    },
    options: {
      response: {
        sample: 50,
        schema: Joi.array().items(bookSchema),
        failAction: 'log'
      }
    }
  });
  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We set options.response.sample to 50 to validate only the first 50 entries.

Validate Status

We validate the response we return according to the response status.

For example, we can write:

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

const dataSchema = Joi.object({
  title: Joi.string().required(),
  author: Joi.string().required(),
});

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });
  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const data = {
        title: 'book',
        author: 'john smith',
      }
      return h.response(data).code(201)
    },
    options: {
      response: {
        status: {
          201: dataSchema,
          202: Joi.object({
            original: dataSchema,
            updated: dataSchema
          })
        }
      }
    }
  });
  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We create the dataSchema with the schema.

Then we set dataSchema as the values of the 201 property.

And we put that inside the Joi.object call for the 202 property.

Since we returned a valid response within the request handler, we get no error in the log.

If we don’t follow the schema, then we get an error logged.

Conclusion

We can validate responses and log validation errors with Hapi and Joi.

Posted in Hapi