Node.js API experiment using hapi.js

Last week, I decided to write a simple application in React Native. In order to do this fast, I needed an API. In the past I would use Sinatra/RoR frameworks (ruby) or express.js (JavaScript) to set up a simple API. Now, I wanted to try something new and this thing had to be written in JavaScript. That is how I started playing with hapi.js.

As an outcome of my week’s work with hapi.js, I’ve created the application backbone which could serve as a starting point of any modern API. The app has:

  • dockerized environment,
  • automated tests using mocha,
  • chai and sinon,
  • measured test coverage,
  • database configuration,
  • eslint linter configuration,
  • API documentation generator using swagger,
  • documentation generator using jsdoc,
  • authentication routes and auth logic with JWT

Feel free to check it out: https://github.com/SoftwareBrothers/hapijs-mongoose-app-bootstrap

In this short article, I will share my feelings about the framework and give practical examples of using it. So, let’s answer the question: “Why people think hapi.js is so awesome?”

Unopinionated framework

First of all, hapi.js in its core is very minimalistic. It has dozens of modules and plugins, but you are free to choose which one you’ll use in your app. Thus, features I describe here are results of installing some specific packages.

Request level validation

This is one of the things I like the most about hapi.js. With the help of hapijs/joi library you can define validation schemas for every single part of the request: header, form data or query string. This is how a route definition could look like:

const userAuthSchema = Joi.object({
  email: Joi.string().email(),
  password: Joi.string().min(6).max(30)
})

{
  path: '/users/auth', method: 'POST',
  handler: usersController.auth,
  options: {
    validate: {
      payload: userAuthSchema
    },
  }
}

And if client passess a request which doesn’t match the schema, he will see descriptive errors:

{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "child \"email\" fails because [\"email\" must be a valid email]"
}

Descriptive errors are not default in hapi.js, but they can be easily turned on: https://github.com/SoftwareBrothers/hapijs-mongoose-app-bootstrap/blob/master/config/server.js#L17

Boom!!!

This is the best name for the library which handles API errors. Also, this is the best error library I’ve found so far. Always, in JavaScript projects, I struggled with different approaches for building and extending Error classes. Hapi.js did a great job by creating a hapijs/boom. You can throw a boom error anywhere and hapi will present this to the user as a json response with the correct status code.

For example, if you throw Boom.unauthorized('invalid password'); This will result in response:

{
    "statusCode": 401,
    "error": "Unauthorized",
    "message": "invalid password"
}

Swagger integration

Hapi has dozens of 3rd party plugins available out there. One of them is a happi-swagger library. Configure it and you'll have your API documented in no time at all. This is how it looks:

Multiple authentication plugins

I decided to use JWT as an authorization method and implemented it in the app, but there are lots of different options containing basic auth, social logins or oauth. They are easy to use, but they don’t do anything under the hood - you have full control of their behaviour. For instance, I added `currentUser` object to `request` parameter in all API handlers: https://github.com/SoftwareBrothers/hapijs-mongoose-app-bootstrap/blob/master/config/auth.js#L12

Websockets

The last thing in the hapi ecosystem I am very excited about (but haven’t tried yet) is Nes - hapi websocket library. It’s designed for both a client and a server. It automatically changes routes so that they can be accessed via websocket connection.

There is more

In the example application I also added a couple of things which are not default in the hapi ecosystem. Those are:

  • Setup for docker and docker-compose in a separate infrastructure folder. It speeds setting up the development environment a lot.
  • I changed testing stack from default hapi’s lab and code libraries to mocha + chai + sinon and added test coverage using instambul. Why? I just really got used to it in other projects and I’m probably too old for drastic changes.
  • I got the habit of documenting my code as much as I can - in JavaScript there is a way of doing that and this way is called jsdoc. So, I added documentation generator + my favourite jsdoc docdash theme.

Hapi summary

After a week with hapi.js, I can recommend it as a starting point of any API application. It’s easy to use, minimalistic and has lots of practical libraries which will make you happy. Of course, I haven’t deployed it on production yet, but - so far - I definitely recommend it.

Interested?

Check out the repo and let me know (in comments) what you think about it. Maybe I’ve missed some amazing js libraries?