Oct 11, 2016

Creating a Node server with Hapi and Joi

Hapi Hapi, Joi Joi

I love that the creators of these libraries must have been Ren & Stimpy fans. The names might be weird, strange, and hilarious, but there is nothing odd or bizarre going on here. Hapi is a node based web server that you can use instead of express. Joi is a validation library that you can use to validate path or query parameters. They integrate very nicely. You end up with very simple and easy to understand code. So lets have a look. You can checkout the github repository https://github.com/veggie2u/hapijoi, or follow the steps here. The repository has the code split into branches. The first step, or branch is called hello.

git checkout hello

To start from scratch, lets create a directory (hapijoi) and cd into that directory. Then…

npm init
npm install --save hapi

This will create your new node project and pull in the dependancies for the hapi server. The answers you gave during the npm init are not too important. This is what I have in the package.json file it created:

{
  "name": "hapijoi",
  "version": "1.0.0",
  "description": "Demo server using hapi and joi",
  "main": "index.js",
  "author": "Chris Ward",
  "license": "ISC",
  "repository": {
    "type": "git",
    "url": "https://github.com/veggie2u/hapijoi"
  },
  "dependencies": {
    "hapi": "15.1.1"
  }
}

Now for the actual server code. For this example, I am just going to use one index.js file. Enter each piece of code to create the most basic of servers. We will go through each part and what it does.

The first thing we do is require the hapi library. Without this, we have no access to the hapi library. We then create a server and start it on port 9000. Pretty simple. There are other things that can be set, but I will leave it to you look them up.

Again, not much going on. We simply start the server and check if there was an error at startup. Once started we output to the console the server name and port so know how to access it on the browser. Now we can do something. The hapi server version of hello world.

This is our first route. Here we describe what url path the server should listen to, and what to do when it finds a match. In this simple case we are just listening to the default path of ‘/’. We get two inputs to our handler, request and reply. Request has several pieces of interesting data, like incoming query parameters. We will look at those in a minute. Reply? Shouldn’t it be response? The reply object is how you interact with the response. By calling the reply object with a string, it will be returned to the browser.

Have you run the server yet? Lets do that and see that it is working. From the console type:

node index.js

Did you get the console message that the server started and the port? You should be able to navigate to that URL in the browser now. Since we are only doing GET requests, the browser will be fine.

http://localhost:9000

Lets expand this a bit. Lets match a route on a different pattern and add a query parameter. If you are just following along with the project, you can see this next section on the branch routes. If you are entering each piece of code, just add it to index.js.

Our path is different. This route will match on /hello. To try this endpoint you will need to stop and restart the server. You can stop it with ctrl-c in the console. Also see that we are using a query param. They are found in the request object under request.query. Notice that we didn’t define any parameters. They will just show up here if entered in the URL.

http://localhost:9000/hello?name=Jim

You should get a “Hello Jim!” to come back. Leave off the name parameter and you will get “Hello world!”.

Now lets do the same thing with a URL parameter.

Again we are going with the /hello path, but we have added {name} to the end, so the url will look like this:

http://localhost:9000/hello/Jim

This will match a route that starts with /hello and adds a /something. We have called this something {name}, so it will show up in the request as request.params.name. Otherwise the code is the same.

So now we can create different routes, and use path and query parameters. How about a little more joi? One thing I really like about the hapi server is the built in joi validation library. Lets look at that. You can switch to the branch ‘validation’, or do the following at the command line to pull in the library.

npm install --save joi

This will add the joi dependency to the project in the package.json file. Next add the joi require to the top of the index.js file.

Then add the following route to the end of the same index.js file.

I have added a new route, /validate so as not to collide with the previous endpoints. Other than that, the only difference from what we have seen is the new config block. There are a few things that can go in here, but to use joi, we add code to the validate block inside it. Because we are dealing with query parameters, we are one more level deep. config / validate / query / actualQueryParam.

In this code we have two query params we are validating. The first is the name, and we identify that it is a string. Joi supports method chaining, so we can say that the name should have at least 3 characters but no more than 10, and that name is required all with a few chained method calls. Similarly, we define age as a number, and an integer with a minimum value of 1, and a maximum value of 50. Note that age is not required. Lets see the results of hitting the new endpoint. Restart the server.

http://localhost:9000/validate

What do you get? A 400 error. A json parseable response tells you that the name is required.

{"statusCode":400,"error":"Bad Request","message":"child \"name\" fails because [\"name\" is required]","validation":{"source":"query","keys":["name"]}}

Now lets give a name and an age.

http://localhost:9000/validate?name=John?age=20
Hello, John! Your age of 20 is valid.

How about hitting the endpoint with an age of 60?

{"statusCode":400,"error":"Bad Request","message":"child \"age\" fails because [\"age\" must be less than or equal to 50]","validation":{"source":"query","keys":["age"]}}

Ok, this is getting long, but one more example. We can also validate path parameters too.

Validating path parameters works just the same, but instead of using a query block, with use a params block. Also note that I didn’t put the required() in there. Because this is a path parameter the only way we can access this code is if there is a name after /validate, so there is no need.

There are a ton of validations that can be done. You can check email, guids, or use a regex. Here is the API reference for Joi https://github.com/hapijs/joi/blob/v9.1.0/API.md

I was thinking… it would be nice to have actual logging instead of that console statement. Oh, and if we could show people our API… hmmmm These are all things that can be accomplished with hapi plugins. I will get to them next time.

Part 2 is up : Adding Logging and Swagger to a Hapi Node Server

About the Author

Chris Ward profile.

Chris Ward

Sr. Consultant

Chris has been programming computers since the Commodore 64 as a kid. He has spent much of his career on the JVM with Java, Groovy, and Grails creating backend services for several companies in the the twin cities. Based on interest and a desire to learn new things, he has branched out into the Javascript realm. The last few years, Chris has been using Angular to create more functional and dynamic front end applications for his clients, as well as creating Node based services on the backend. Recently, just for fun, Chris has been getting into robotics and Iot.

One thought on “Creating a Node server with Hapi and Joi

Leave a Reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]