Are you Hapi(.js)? (Part 1/2)
Hapi.js is “A rich framework for building applications and services”. In the present article I will make some experiments with Hapi.js framework. I will show some simple scenarios using the framework. As a reference I use Make Me Hapi, which is a self-guided workshop to teach Hapi. I found it extremely useful for learning the basics of Hapi.js, and in this article I explain the official solutions for the exercises of the workshop. I modified the official solutions in some cases, therefore they will not always be run by the workshop.
This article is the first part of a two-part series.
Keywords: Node.js, NPM, Javascript, Hapi.js, HTTP, Linux, Ubuntu, Template Engine, Inert, Path, Vision, Handlebars.js, H2o2, Routing, Static files, Directories, Templates, Proxy server
DISCLAIMER: THE VIEWS AND OPINIONS EXPRESSED IN THIS ARTICLE ARE THOSE OF THE AUTHOR AND DO NOT REFLECT THE OFFICIAL POLICY OR POSITION OF THE EMPLOYER OF THE AUTHOR. THE ARTICLE IS NOT ENDORSED BY, DIRECTLY AFFILIATED WITH, MAINTAINED, AUTHORIZED, OR SPONSORED BY ANY CORPORATION OR ORGANIZATION. THE INFORMATION CONTAINED ON THIS ARTICLE IS INTENDED SOLELY TO PROVIDE GENERAL GUIDANCE ON MATTERS OF INTEREST FOR THE PERSONAL USE OF THE READER, WHO ACCEPTS FULL RESPONSIBILITY FOR ITS USE. ALTHOUGH THE AUTHOR HAS MADE EVERY EFFORT TO ENSURE THAT THE INFORMATION IN THIS ARTICLE WAS CORRECT AT THE TIME OF THE WRITING, THE AUTHOR DOES NOT ASSUME AND HEREBY DISCLAIM ANY LIABILITY TO ANY PARTY FOR ANY LOSS, DAMAGE, OR DISRUPTION CAUSED BY ERRORS OR OMISSIONS, WHETHER SUCH ERRORS OR OMISSIONS RESULT FROM NEGLIGENCE, ACCIDENT, OR ANY OTHER CAUSE.
Programming languages, technology
This article involves the following programming languages and technology:
- Javascript
- Node.js
- hapi.js
- HTTP
- Linux
The following node modules and Javascript libraries are used:
- hapi.js
- inert
- path (core module)
- vision
- handlebars.js
- h2o2
Table of Contents
The article is divided into the following sections:
- Preparation, prerequisites
- 1.: Simple server
- 2.: Simple routing
- 3.: Serving static files
- 4.: Directories
- 5.: Templates
- 6.: Proxies
- 7.: Helper scripts
- Closing words
- License
- References
Preparation, prerequisites
The development environment I use is based on Ubuntu Linux 14.04.5 LTS (trusty)
, Nodejs v11.3.0
, npm 6.4.1
, nvm 0.33.11
. I assume these are installed in your system. I’m using Cloud 9 environment.
To intall Hapi, you need to run
npm install hapi
Version of Hapi I use is 17.8.1
.
In these examples I use process.env.IP
and process.env.PORT
instead of localhost
and 8080
. This is because of the Cloud 9 environment.
1.: Simple server
First this is an example of a simple web server.
The app will show a “Hello hapi” text in the browser. It is important to observe how to start the server. It is put to an async function, and the server is started with await server.start()
.
Further reading:
2.: Simple routing
In the previous example '/'
was provided as path
attribute. Paths can contain named parameters. In the example below, the parameter name is name
and it is returned by accessing with request.params.name
.
About encodeURIComponent()
: according to the documentation, it “encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two “surrogate” characters).”.
Further reading:
3.: Serving static files
In order to send static files to the client an additional module is needed. This module is inert
(current version is 5.1.2
), which is installed with:
npm install inert
For this example need an additional module is used, which is the core modulepath
. This example also contains an HTML file.
The server serves index.html
to the client, when it is accessed with path /
.
Solution 1
I needed to modify the coding because inter module must be registered for the server. Therefore server.route()
calls are put in the initialization. In the end, I put the call to Hapi.Server()
also in the initialization.
It is important to care about the proper file path on the server, for this we need to use Path.join(__dirname, 'index.html');
.
I left the code from the previous example here, so that paths like /name
are also served.
Solution 2
As an alternative solution it is possible to configure a base path in your server and only pass relative path. The base path can be specified in routes
. This way I can use index.html
without specifying the absolute path.
4.: Directories
With the directory handler it is possible to access a file in a directory with a different path in the URL. It makes possible to specify one route to serve multiple files. The below route will respond to any request by looking for a matching filename in /foo/bar/baz
directory. But it would respond with HTTP 403 if the directory itself is accessed without a file in the path. With the highlighted part of the code this can be fixed:
server.route({
path: '/foo/bar/baz/{param*}',
method: 'GET',
handler: {
directory: {
path: Path.join(__dirname, 'public'),
index: ['index.html', 'default.html']
}
}
});
When there is no index file available, inert can display the contents of the directory as a listing page. You can enable that by setting the listing
property to true
like so:
server.route({
path: '/foo/bar/baz/{param*}',
method: 'GET',
handler: {
directory: {
path: Path.join(__dirname, 'public'),
listing: true
}
}
});
(The above two features are not included in the example below.)
In this example below we have a file.html
in the /public
directory of the server, and access this file with the /foo/bar/baz/file.html
path. (We could access other files in the same /public
path as well.)
5.: Templates
This exercise shows the templating possibilities using Hapi.js. For this two additional modules are used:
- vision
- handlebars.js
Let’s install these:
node install vision handlebars
For this example Inert module is not needed.
The vision plugin needs to be registered for the server:
// Register Vision plugin
await server.register(Vision);
Also Handlebars module needs to be registered so that the templates will work. Handlebars is the engine responsible for rendering templates with an extension of .html
.
// Register Handlebars to handle templates
server.views({
engines: {
html: Handlebars
},
path: Path.join(__dirname, 'templates')
});
In this example the server is called with /?name=World
and it will process the templates in the /templates
directory. As it can be seen, the index.html
is a special HTML file which contains references which can be processed by Handlebars.js. With {{query.name}}
the name
parameter is referenced, and the content of it is put to the HTML page, which is sent back to the client.
The implementation is the following:
6.: Proxies
In this exercise a proxy server is implemented. For this module h2o2
is needed.
npm install -g h2o2
H2O2 is a proxy handler for Hapi.js. For this exercise there are 2 scripts. The first server is a proxy server running on port 8080 and this connects to the other server. When the proxy server is accessed with path /proxy
it will connect to the another server. The other server is running on port 65535. This one will serve an HTML page. The two servers need to be run at the same time.
In the following code snippets the index.html
file is served by the target server — under target server I mean the server which is accessed by the proxy server.
The script makemehapi-exercise-6-proxy.js
contains the implementation of the proxy server. This will require h2o2
, and set a proxy in the handler of the server route /proxy
. This script needs to be run with node makemehapi-exercise-6-proxy.js
.
The script makemehapi-exercise-6-target.js
is the “target” server. This needs to be run with node makemehapi-exercise-6-target.js 65535
.
In computer networks, a proxy server is a server (a computer system or an application) that acts as an intermediary for requests from clients seeking resources from other servers. A client connects to the proxy server, requesting some service, such as a file, connection, web page, or other resource available from a different server and the proxy server evaluates the request as a way to simplify and control its complexity.
Communication between two computers (shown in grey) connected through a third computer (shown in red) acting as a proxy. Bob does not know to whom the information is going, which is why proxies can be used to protect privacy. (Source: Wikipedia)
7.: Helper scripts
Further reading: Views, section “View helpers”.
In Hapi.js it is possible to define view helpers. These are helper functions which can be used in the templates. In the following example the application prints out a fruit name, for which a parameter is received from the URL.
In the example below, helper.js
should be in folder helpers
. This helpers folder is registered in the server with the following:
server.views({
engines: {
html: Handlebars
},
path: __dirname,
helpersPath: Path.join(__dirname, 'helpers')
});
The helper an implementation of a function which returns a value, which will be then used in the template, when the helper is referenced. For example, a simple helper.js
file in the helpers
folder looks like the following:
module.exports = function (context) {
value = ""; // ... implementation here ... // returning the value
return value;
};
Context is passed to the helper method.
In the template this is referenced with the name of the helper. For example, in a p
tag:
<p>{{helper}}</p>
In the helper implementation, with the context.data.root.query
the query parameters in the URL can be accessed. For example in the URL
https://localhost/?fruitName=3
the value of fruitName
can be accessed with:
context.data.root.query.fruitName
The example of helpers looks like the following. In this example fruits.js
should be in the helpers
folder.
Closing words
I hope you have learned something new today. Thank you for reading this article.
License
The code snippets used in this article are based on the original solutions for the workshop, therefore I reproduce here the license of makemehapi workshop. This is relevant for the code snippets posted in this article.
Copyright (c) 2012-2014, Walmart and other contributors.
All rights reserved.Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
* The names of any contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *The complete list of contributors can be found at: https://github.com/hapijs/makemehapi/graphs/contributors
Original version of the License: