Node.js API with Hapi.js

Daniel Cacheta
White Prompt Blog
Published in
6 min readSep 6, 2022

--

It is very common to hear about Node.js projects with Express Web Framework. Node.js was first introduced to the world in 2009 at the annual JavaScript Conference, and has become very popular over the years, later being adopted by leading technology companies. Some of its positive features include its speed and performance, a vast number of open-source libraries and free tools, capability of code sharing and reuse, and better efficiency as well as overall developer productivity.

At White Prompt, we are collaborators who collectively put our heads together with our clients to create unique solutions. This includes working efficiently and in the most productive way possible. We understand your goals and get down to the nitty gritty mapping out the process. With our client Aleign, our team of senior developers used cutting-edge technologies, including Node.js for the backend. Read more about our technical approach on our website.

White Prompt Blog

Quite a few different frameworks have been created to provide numerous features during the mobile and app development process; Express is one of those. One upside to Express is that it provides fundamental benefits for web applications without hindering any benefits that come with Node.js. And when you are first getting introduced to Node.js frameworks, Express is among the first you will hear of. This does not always mean it is the best, however, depending on your application.

In comparison to Express, Hapi is an alternative, originally designed by Walmart Labs to create APIs for high scale needs such as Black Friday. Walmart Labs faced a few main issues with Express that included the possibility of conflicting routes and the ordering of middleware that could cause potential problems with other team member’s work. Hapi allowed them to create a safer and more isolated environment for each developer’s work.

Hapi worked so well during Black Friday 2013 that the Walmart Labs team created the hashtag #nodebf on Twitter to share their experience, some code samples and charts displaying flat CPU and Memory consumption without peaking with the significant traffic during the day.

Flat memory charts from #nodebf
Flat memory charts from #nodebf

Hapi is open source and its GitHub repository can be found here.

The examples from this article can be found here.

Installing hapi

To install Hapi, create a directory for your project and run the npm init command on the directory to initialize the package.json file.

Run npm install @hapi/hapi to install the latest hapi version.

After installing the dependency, it can be imported to your project using const Hapi = require('@hapi/hapi'); or import { server } from '@hapi/hapi'; command if you are using ES6 in your project, which is the one we will use in this example.

Configuring TypeScript

In this example we will use TypeScript, which requires installing some devDependencies, shown below.

npm install typescript ts-node @types/hapi @types/hapi__hapi nodemon --save-dev

With this in place, we can configure the npm start to run the project with nodemon:

"scripts": {
"start": "npx nodemon src/index.ts"
},

Creating a basic server

The npm start command is pointing to the src/index.ts file. For a basic server, it will have a structure like this:

When we run npm start, the API will be listening on port 3000. If we try to access it, we will get a 404 Not Found error, as it didn’t find any routes configured on the server.

Screenshot of the “404 Not Found” error when it doesn’t have any routes configured
Screenshot of the “404 Not Found” error when it doesn’t have any routes configured

Adding routes

Routes can be added using the .route() method from hapi, which configures the HTTP method, path, and handler for that endpoint.

For a Hello World example listening on the base path, the code would be as simple as:

exampleServer.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello World!';
}
});

And when we try to access the same address again, we get the Hello World response:

Response example returning “200 OK” now that the route is defined
Response example returning “200 OK” now that the route is defined

For scenarios where the API expects an optional parameter on the URL, hapi allows us to define the parameter with a question mark after its name, and then get the value from request.params property.

This is the route configuration example to call an endpoint with the following format:

exampleServer.route({
method: 'GET',
path: '/hello/{name?}',
handler: (request, h) => {
const name = request.params.name ?? 'stranger';
return `Hello ${name}!`;
}
});

The example above would be listening to the http://localhost:3000/hello/Daniel endpoint. Now, let’s say we are using Query Parameters instead to call http://localhost:3000/hi?name=Daniel.

The configuration is slightly different for Query Parameters, getting the value from request.query instead:

exampleServer.route({
method: 'GET',
path: '/hi',
handler: (request, h) => {
const name = request.query.name ?? 'stranger';
return `Hi ${name}!`;
}
});

For a POST where the content is in the request body instead of the URL, and there are not any validations initially, the route configuration would look like this:

exampleServer.route({
method: 'POST',
path: '/signup',
handler: (request, h) => {
const payload = request.payload as SignUp;
return `Welcome ${payload.username}!`;
}
});

Validating the request

For this next step, let’s add some validations using Joi. Joi is a separate package that needs to be installed running npm install joi.

Now the same /signup endpoint, with validations for the payload to configure username and password as required fields, and to make sure that the password has at least 5 characters, would then be changed to:

exampleServer.route({
method: 'POST',
path: '/signup',
handler: (request, h) => {
const payload = request.payload as SignUp;
return `Welcome ${payload.username}!`;
},
options: {
validate: {
payload: Joi.object({
username: Joi.string().required(),
password: Joi.string().min(5).required()
})
}
}
});

Now the API is able to validate the values that have been sent. If the request doesn’t match the validations, the API won’t run the handler function and it will return a “400 Bad Request” instead, shown here:

Scenario where the password doesn’t meet the minimum characters returns a “400 Bad Request”
The scenario where the password doesn’t meet the minimum characters returns a “400 Bad Request”

Besides the payload, it is also possible to add validation to other properties from the request (headers, params, query, state) or even the response, adding a failAction that can return an error, log, ignore, or even run a custom action using the signature async function (request, h, err) when the response validation failed.

options: {
response: {
schema: Joi.object({
id: Joi.number().required(),
name: Joi.string().required()
}),
failAction: 'log'
}
}

More details about validations can be found here.

Conclusion

I had a great first impression of hapi!

When you consider the examples I’ve already tried and the well-organized documentation it provides, hapi seems to be pretty flexible with its many configurations, the existing plugins that can be added, or even allowing developers to create new plugins as they are required by their projects.

I’ll include some links down below for those of you who might be interested in learning more about it as well.

Before we wrap up this article, I’d like to invite you to follow White Prompt’s social networks to read our future articles and watch our videos: Medium, YouTube, LinkedIn, Instagram, Facebook and our Website.

Be sure to check out our other articles on Medium as we hope this article was interesting for you.

References

Express.js - https://expressjs.com/

Hapi.js website - https://hapi.dev/

Hapi.js routing configs - https://hapi.dev/tutorials/routing

Hapi.js validation - https://hapi.dev/tutorials/validation

Hapi.js existing plugins - https://hapi.dev/tutorials/plugins

Hapi.js configure new plugins - https://hapi.dev/tutorials/plugins

GitHub hapi.js - https://github.com/hapijs/hapi

GitHub project for this article - https://github.com/danielcacheta-whiteprompt/node.js-with-hapi

Hapi.js in Action - https://livebook.manning.com/book/hapi-js-in-action/

YouTube videos explaining and showing some of the hapi features:

https://youtu.be/2lprC0yYeFw (in English)

https://youtu.be/wik-pzLcRG4 (in Portuguese)

--

--