# hapi Documentation
# Source: https://hapi.dev/tutorials/expresstohapi/?lang=en_US

Tutorials

Languages:

en-US pt-BR ko-KR tr-TR zh-CN

  * [Getting Started](/tutorials/gettingstarted/?lang=en_US)

* * *

  * [Express Migration](/tutorials/expresstohapi/?lang=en_US)

* * *

  * [Authentication](/tutorials/auth/?lang=en_US)

* * *

  * [Caching](/tutorials/caching/?lang=en_US)

* * *

  * [Cookies](/tutorials/cookies/?lang=en_US)

* * *

  * [Logging](/tutorials/logging/?lang=en_US)

* * *

  * [Plugins](/tutorials/plugins/?lang=en_US)

* * *

  * [Routing](/tutorials/routing/?lang=en_US)

* * *

  * [Server Methods](/tutorials/servermethods/?lang=en_US)

* * *

  * [Serving Static Files](/tutorials/servingfiles/?lang=en_US)

* * *

  * [Testing](/tutorials/testing/?lang=en_US)

* * *

  * [Validation](/tutorials/validation/?lang=en_US)

* * *

  * [Views](/tutorials/views/?lang=en_US)

* * *

  * [Community Tutorials](/tutorials/community/?lang=en_US)



# Express Migration

_This guide is compatible with hapi v17 and newer_

##  Overview

This Express to hapi guide will show you how to take what you know how to do in Express, and do it in hapi. While Express relies heavily on middleware for much of its functionality, hapi has more built into the core. Body parsing, cookie handling, input/output validation, and HTTP-friendly error objects are already built-in to the hapi framework. For additional functionality, hapi has a robust selection of plugins in its core ecosystem. hapi is also the only framework that doesn't rely on outside dependencies. Every dependency is managed by the core hapi team, which makes security and reliability some of hapi's greatest strengths.

##  Setup

###  Installation

Express:  
`npm install express`

hapi:  
`npm install @hapi/hapi`

###  Creating a Server

Express:
    
    
    var express = require('express');
    var app = express();
    
    app.listen(3000, function () {
      console.log('Server is running on port 3000');
    }));
    

hapi:
    
    
    const Hapi = require('@hapi/hapi');
    
    const init = async () => {
    
        const server = Hapi.server({
            port: 3000,
            host: 'localhost'
        });
    
        await server.start();
        console.log('Server running on port 3000');
    };
    
    init();
    

Unlike Express, in hapi you create a server object that will be the focal point of your application. The properties set in the server object will determine how your application behaves. Once you create your server object, you can start your server by calling `server.start()`.

##  Routes

Routes in hapi get called in a specific order, so you will never have an issue where two routes are conflicting with one another. Routes are called from most specific to least specific. For example, a route with a path `'/home'` will be called before `'/{any*}'`.

Lets look at how to set up a basic route in hapi:

Express:
    
    
    app.get('/hello', function (req, res) {  
      res.send('Hello World!');
    });
    

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

To create a route, Express has the structure of `app.METHOD(PATH, HANDLER)` and hapi has the structure `server.route({METHOD, PATH, HANDLER})`. The method, path, and handler are passed to the hapi server as an object. As you can see, to return a string in Express, you call `res.send()`, whereas in hapi, you simply return the string.

###  Methods

hapi can use all the route methods that Express can, except `HEAD`. hapi also has the ability to use multiple methods on a single route object. For example:
    
    
    server.route({
        method: ['PUT', 'POST'],
        path: '/',
        handler: function (request, h) {
    
            return 'I did something!';
        }
    });
    

To use all available methods, like in Express `app.all()`, use `method: '*'`.

###  Path

Like in Express, the path option in hapi must be a string, which can also contain parameters. Parameters in Express are preceded by `:`, such as: `'/users/:userId'`. In hapi, you would put the parameter in curly braces, like: `path: '/users/{userId}'`.

###  Parameters

You saw above how hapi handles simple parameters as compared to Express. Both hapi and Express handle optional parameters the same way. Just like Express, to make a parameter optional in hapi, just include a `?` after the parameter: `path: '/hello/{user?}`.

Accessing the parameters in hapi is very similar to Express. As you know, in Express, the parameters are populated in the `req.params` object. In hapi, the parameters are available via the `request.params` object. Here is an example of both:

Express:
    
    
    app.get('/hello/:name', function (req, res) {
    
        const name = req.params.name
        res.send('Hello ' + name);
    }); 
    

hapi:
    
    
    server.route({
        method: 'GET',
        path: '/hello/{name}',
        handler: function (request, h) {
    
            const name = request.params.name;
            return 'Hello ' + name
        }
    });
    

Query parameters are also similar in both frameworks. In Express, they are available via `req.query` and hapi they are available via `request.query`.

###  Handler

There are differences in the way Express and hapi structure their route handlers. Unlike Express, which has a handler with parameters of `req` and `res`, hapi has a handler with parameters of `request` and `h`. The second parameter, `h` is the response toolkit, which is an object with several methods used to respond to the request.

Here is an example of route with a handler that redirects to another route in Express and hapi:

Express:
    
    
    app.get('/home', function (req, res) {
    
        res.redirect('/');
    });
    

hapi:
    
    
    server.route({
        method: 'GET',
        path: '/home',
        handler: function (request, h) {
    
            return h.redirect('/');
        }
    });
    

Both routes will redirect to the `'/'` route. Express uses the response method `res.redirect` whereas hapi uses `h.redirect` which is part of the response toolkit. There are Express response methods that hapi can accomplish by just using `return`. Some of these methods include `res.send` and `res.json`. Here is an example of how hapi will respond with JSON data:
    
    
    server.route({
        method: 'GET',
        path: '/user',
        handler: function (request, h) {
    
            const user = {
                firstName: 'John',
                lastName: 'Doe',
                userName: 'JohnDoe',
                id: 123
            }
    
            return user;
        }
    });
    

hapi has the functionality to respond with JSON data by default. The only thing you have to do is just return a valid JavaScript object and hapi will take care of the rest for you.

##  Middleware vs Plugins and Extensions

To extend its functionality, Express uses middleware. Middleware essentially is a sequence of functions using callbacks to execute the next function. The issue with this is as your application grows in size and complexity, the order at which middleware executes becomes more crucial and more difficult to maintain. Having a middleware execute before one it is dependant on will cause your application to fail. hapi fixes this issue with its robust plugin and extension system.

Plugins allow you to break your application logic into isolated pieces of business logic, and reusable utilities. Each plugin comes with its own dependencies which are explicitly specified in the plugins themselves. This means you don't have to install dependencies yourself to make your plugins work. You can either add an existing hapi plugin, or write your own. For a more extensive tutorial on plugins, please see the [plugins](/tutorials/plugins/?lang=en_US) tutorial.

Each request in hapi follows a predefined path, the request lifecycle. hapi has extension points that let you create custom functionality along the lifecycle. Extension points in hapi let you know the precise order at which your application will run. For more info, please see the hapi [request lifecycle](/api/#request-lifecycle).

###  Extension Points

hapi has 7 extension points along the request lifecycle. In order, they are `onRequest`, `onPreAuth`, `onCredentials`, `onPostAuth`, `onPreHandler`, `onPostHandler`, and `onPreResponse`. To add a function to an extension point, you call `server.ext()`. Lets look at an example:
    
    
    server.ext('onRequest', function (request, h) {
    
        request.setUrl('/test');
        return h.continue;
    });
    

This function will run at `onRequest`, which is the first extension point. `onRequest` is run just after the server receives the request object, before the route lookup. What this function will do is reroute all requests to the `'/test'` route.

###  Creating a Plugin

As you know, you can write your own middleware in Express. The same is true with hapi plugins. A plugin is an object with required `name` and `register` properties. The `register` property is a function with the signature of `async function (server, option)`. Lets look at how to create a basic plugin:

Express:
    
    
    const getDate = function (req, res, next) {
    
        req.getDate = function() {
    
            const date = new Date();
            return date;
        };
    
        next();
    };
    

hapi:
    
    
    const getDate = {
        name: 'getDate',
        version: '1.0.0',
        register: async function (server, options) {
    
            const currentDate = function() {
    
                const date = new Date();
                return date;
            };
    
            server.decorate('toolkit', 'getDate', currentDate);
        }
    };
    

The hapi plugin will save the current date in `h.getDate()`. We can then use this in any of our route handlers.

###  Loading a Plugin

In Express, you load middleware by calling the `app.use()` method. In hapi, you call the `server.register()` method. Lets load the plugin we created in the previous section:

Express:
    
    
    app.use(getDate);
    

hapi:
    
    
    await server.register({
        plugin: getDate
    });
    

You can also provide options to your plugin by setting the `options` property on `server.register()`.

###  Options

You can add options to Express middleware by exporting a function that accepts an options parameter, which then returns the middleware. In hapi, you set the options when you register the plugin. Lets have a look:

Express:
    
    
    module.exports = function (options) {
        return function getDate(req, res, next) {
    
            req.getDate = function() {
    
                const date = 'Hello ' + options.name + ', the date is ' + new Date();
                return date;
            };
    
            next()
        };
    };
    

hapi:
    
    
    const getDate = {
        name: 'getDate',
        version: '1.0.0',
        register: async function (server, options) {
    
            const currentDate = function() {
    
                const date = 'Hello ' + options.name + ', the date is ' + new Date();
                return date;
            };
    
            server.decorate('toolkit', 'getDate', currentDate);
        }
    };
    

To get access to the options in hapi, you simply refer to the `options` object when you create the plugin:

Express:
    
    
    const getDate = require('./mw/getDate.js');
    
    app.use(getDate({ name: 'Tom' }));
    

hapi:
    
    
    server.register({
        plugin: getDate,
        options: {
            name: 'Tom'
        }
    })
    

##  body-parser

hapi has parsing abilities built into its core. Unlike Express, you do not need middleware to parse payload data. In fact, you may need to install up to four additional middlewares in Express depending on what kind of data you would like to parse. In hapi the payload data, whether its JSON or plain text, is readily available in the `request.payload` object. Here is a side by side comparison of parsing simple payload data:

Express:
    
    
    var bodyParser = require('body-parser');
    
    app.use(bodyParser.urlencoded({extend: true}));
    
    app.post('/hello', function (req, res) {  
    
      var name = req.body.name
      res.send('Hello ' + name);  
    });
    

hapi:
    
    
    server.route({
        method: 'POST',
        path: '/hello',
        handler: function (request, h) {
    
            const name = request.payload.name;
            return `Hello ` + name;
        }
    });
    

To parse a JSON object in express, you have to specify it:
    
    
    app.use(bodyParser.json());
    

JSON parsing is built into hapi, so there are no further steps needed.

##  cookie-parser

Setting and parsing cookies in Express require you to install the `cookie-parser` middleware. hapi has cookie functionality built right into the core, so there is no need to install additional middleware. To use cookies in hapi, you first configure the cookie with `server.state()`. Lets have a look:
    
    
    const Hapi = require('@hapi/hapi');
    
    const server = Hapi.server({ port: 8000 });
    
    server.state('data', {
        ttl: null,
        isSecure: true,
        isHttpOnly: true
    });
    

###  Setting a Cookie

Once the cookie is configured, you can now set the cookie with `h.state()`. Here is an example:

Express:
    
    
    var express = require('express');
    var app = express();
    var cookieParser = require('cookie-parser');
    
    app.use(cookieParser());
    
    app.get('/', function(req, res) {
        
        res.cookie('username', 'tom', { maxAge: null, secure: true, httpOnly: true });
        res.send('Hello');
    });
    

hapi:
    
    
    const Hapi = require('@hapi/hapi');
    
    const server = Hapi.server({ port: 8000 });
    
    server.state('username', {
        ttl: null,
        isSecure: true,
        isHttpOnly: true
    });
    
    server.route({
        method: 'GET',
        path: '/',
        handler: function (request, h) {
    
            h.state('username', 'tom');
            return h.response('Hello');
        }
    });
    

In express, you configure cookie with the `options` object in `res.cookie`. In hapi, the cookie config is saved to the server object with `server.state`. You then use `h.state()` to attach data to the cookie.

###  Getting a Cookie Value

To get a cookie value in hapi, you call `request.state`. Lets have look:

Express:
    
    
    var express = require('express');
    var app = express();
    var cookieParser = require('cookie-parser);
    
    app.use(cookieParser());
    
    app.get('/', (req, res) => {
        
        res.cookie('username', 'tom', { maxAge: null, secure: true, httpOnly: true })
        res.send(req.cookies.username);
    });
    

hapi:
    
    
    const Hapi = require('@hapi/hapi');
    
    const server = Hapi.server({ port: 8000 });
    
    server.state('username', {
        ttl: null,
        isSecure: true,
        isHttpOnly: true
    });
    
    server.route({
        method: 'GET',
        path: '/',
        handler: async (request, h) => {
    
            h.state('username', 'tom');
            return h.response(request.state.username);
        }
    });
    

##  Passport -> bell

In Express, third party authentication is handled with Passport. In hapi, you use the [bell](/module/bell) module for third party authentication. `bell` has over 30 predefined configurations for OAuth providers including Twitter, Facebook, Google, GitHub, and more. It will also allow you to set up your own custom provider. For a complete list, please see the [bell providers documentation](https://github.com/hapijs/bell/blob/master/Providers.md). `bell` was developed and is maintained by the core hapi team, so you know stability and reliability won't be an issue. Lets look how to authenticate using your Twitter credentials:

Express:
    
    
    npm install passport passport-twitter
    

  

    
    
    var passport = require('passport');
    var TwitterStrategy = require('passport-twitter').Strategy
    
    passport.use(new TwitterStrategy({
        consumerKey: TWITTER_CONSUMER_KEY,
        consumerSecret: TWITTER_CONSUMER_SECRET,
        callbackURL: '/auth/twitter/callback'
    },
        function(token, tokenSecret, profile, cb) {
            User.findOrCreate({ twitterId: profile.id }, function (err, user) {
                return cb(err, user);
            }
        }
    ));
    
    passport.seralizeUser(function(user, cb) {
        cb(null, user);
    })
    
    passport.deserializeUser(function(user, cb) {
        cb(null, obj);
    })
    
    app.get('/auth/twitter', passport.authenticate('twitter'));
    app.get('/auth/twitter/callback', passport.authenticate('twitter', { failureRedirect: '/login'}),
        function(req, res) {
    
            res.redirect('/');
        });
    

hapi:
    
    
    npm install '@hapi/bell'
    

  

    
    
    const Hapi = require('@hapi/hapi');
    const Bell = require('@hapi/bell');
    
    const server = Hapi.server({ port: 8000 });
    
    await server.register(Bell);
    
    server.auth.strategy('twitter', 'bell', {
        provider: 'twitter',
        password: 'cookie_encryption_password_secure',
        clientId: TWITTER_CONSUMER_KEY,
        clientSecret: TWITTER_CONSUMER_SECRET,
        isSecure: false
    });
    
    server.route({
        method: '*', 
        path: '/auth/twitter',            // The callback endpoint registered with the provider
        handler: function (request, h) {
    
            if (!request.auth.isAuthenticated) {
                return `Authentication failed due to: ${request.auth.error.message}`;
            }
            
            // Perform any account lookup or registration, setup local session,
            // and redirect to the application. The third-party credentials are
            // stored in request.auth.credentials. Any query parameters from
            // the initial request are passed back via request.auth.credentials.query.
    
            return h.redirect('/home');
        },
        options: {
            auth: {
              strategy: 'twitter',
              mode: 'try'
            }
        }
    });
    

To use bell, simply register the plugin and configure the strategy with `server.auth.strategy`.

`provider` is the name of the third-party provider.

`password` is the cookie encryption password.

`clientId` is the OAuth client identifier, which is available from the provider.

`clientSecret` is the OAuth client secret, which is available from the provider.

`isSecure` sets the cookie secure flag. For production, this should be set to `true`, which is the default value.

##  express-validator -> joi

To validate data in Express, you make use of the `express-validator` plugin. One of the biggest drawbacks to `express-validator` is that while you can validate a request, there is no clear way of validating a response. In hapi, you use the [joi](https://joi.dev) module, which can validate requests and responses with ease. Joi allows you to create your own validations with a simple and clean object syntax. For a more in-depth look at validation in hapi, please see the [validation](/tutorials/validation/?lang=en_US) tutorial.

###  Input Validation

Input validation allows you to validate any input data coming into the server, whether its parameters, payload, etc. Here is a look at how to validate a blog post entry in Express and hapi:

Express:
    
    
    npm install express-validator
    

  

    
    
    const bodyParser = require('body-parser');
    const expressValidator = require('express-validator');
    
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(expressValidator())
    
    app.post('/post', function (req, res) {
    
      req.check('post', 'Post too long').isLength({ max: 140 });
    
      let errors = req.validationErrors();
      if (errors) {
        res.status(400).send(errors);
      } else {
        res.send('Blog post added!') 
      }
    });
    

hapi:
    
    
    npm install joi
    

  

    
    
    const Joi = require('joi')
    
    server.route({
        method: 'POST',
        path: '/post',
        handler: (request, h) => {
    
            return 'Blog post added!';
        },
        options: {
            validate: {
                payload: Joi.object({
                    post: Joi.string().max(140)
                })
            }
        }
    });
    

First you install `joi`, then require it in your project. To validate the input data, you specify what data type you are expecting, then set rules on that data. In this case, `post` will be a `string` with a maximum number of characters of 140. In `joi` you can string rules together like:
    
    
    Joi.string().min(1).max(140).
    

###  Output Validation

As stated above, there is no clear way of doing response validation with `express-validator`. With `joi`, response validation is fast and simple. Lets have a look:

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()
    });
    
    server.route({
        method: 'GET',
        path: '/books',
        handler: async function (request, h) {
    
            return await getBooks();
        },
        options: {
            response: {
                schema: Joi.array().items(bookSchema),
                failAction: 'log'
            }
        }
    });
    

This route will return a list of books. In the `options` route property, we can specify what rules the list of books should follow. By setting the `failAction` to `log`, if there is an error, the server will log the error.

##  app.set('view engine') -> vision

hapi has extensive support for template rendering, including the ability to load and leverage multiple templating engines, partials, helpers (functions used in templates to manipulate data), and layouts. Express enables views capabilities by using `app.set('view engine')`, where hapi's capabilities are provided by the [vision](/module/vision) plugin. For a more extensive tutorial on views in hapi, please see the [views](/tutorials/views/?lang=en_US) tutorial.

###  Setting the View Engine

Setting the views engine in express looks like the following:
    
    
    app.set('view engine', 'pug');
    

To set the views engine in hapi, you first must register the vision plugin, then configure `server.views`:
    
    
    await server.register(require('@hapi/vision'));
    
    server.views({
        engines: {
            pug: require('pug')
        },
        relativeTo: __dirname,
        path: 'views'
    });
    

By default, Express will look for views or templates in the `views` folder. In hapi, you specify where the views are located using the `relativeTo` and `path` properties. Like Express, hapi supports a wide variety of templating engines, such as pug, ejs, handlebars, etc.

hapi has many more configurable options in `server.views`. To see the full list of capabilities, please go to the [views](/tutorials/views/?lang=en_US) tutorial.

###  Rendering a View

To render a view in Express, you would call `res.render()`. hapi, by way of vision, has two methods for rendering views, `h.view` and the view handler. Lets look at how to do both.  
First, rendering a view in Express:
    
    
    app.get('/', function (req, res) {
    
        res.render('index', { title: 'Homepage', message: 'Welcome' });
    });
    

Using `h.view` in hapi:
    
    
    server.route({
        method: 'GET',
        path: '/',
        handler: function (request, h) {
    
            return h.view('index', { title: 'Homepage', message: 'Welcome' });
        }
    });
    

And using the view handler in hapi:
    
    
    server.route({
        method: 'GET',
        path: '/',
        handler: {
            view: {
                template: 'index',
                context: {
                    title: 'Homepage',
                    message: 'Welcome'
                }
            }
        }
    });
    

To pass context in `h.view`, you pass an object as the second parameter. To pass context in the view handler, you use the `context` key.

##  express.static() -> inert

hapi gets its ability to serve static content from a plugin called [inert](/module/inert). inert provides new handler methods for serving static files and directories, as well as adding a `h.file()` method to the response toolkit. For a more in-depth tutorial of server static files in hapi, please see the [serving static files](/tutorials/servingfiles/?lang=en_US) tutorial.

###  Serving Single Files

In Express, you would use the `res.sendFile` method to return a single file. In hapi, you can either use the `h.file()` method or the file handler, which is available via [inert](/module/inert). Once you register the inert plugin, you will be able to serve your static files:

Express:
    
    
    app.get('/image', function (req, res) {
    
        res.sendFile('image.jpg', {root: './public'});
    });
    

hapi with `h.file()`:
    
    
    const server = new Hapi.Server({
        port: 3000,
        routes: {
            files: {
                relativeTo: Path.join(__dirname, 'public')
            }
        }
    });
    
    await server.register(require('@hapi/inert'));
    
    server.route({
        method: 'GET',
        path: '/image',
        handler: function (request, h) {
    
            return h.file('image.jpg');
        }
    });
    

hapi with file handler:
    
    
    const server = new Hapi.Server({
        port: 3000,
        routes: {
            files: {
                relativeTo: Path.join(__dirname, 'public')
            }
        }
    });
    
    await server.register(require('@hapi/inert'));
    
    server.route({
        method: 'GET',
        path: '/image',
        handler: {
            file: 'image.jpg'
        }
    });
    

To serve static files in hapi, you first must tell hapi where the static files are located. You do this by configuring the `server.options.routes` object. You set the `relativeTo` to the folder where the files are located, much like you do in the options object of the `res.sendFile` in Express. Next, you need to register the inert plugin. This will give you access to the methods that allows you to serve static files. Now in your route handler, you can use the `h.file()` method or the file handler to server your static file.

###  Static File Server

To set up a static file server in Express, you would use the `express.static()` middleware. In hapi, you use the file handler made available by the [inert](/module/inert) plugin. You would setup the server in the same way as you did to serve a single static file, by telling where the files are located. You then would setup a route to catch all of the requests and return the correct files. Lets have a look:

Express:
    
    
    app.use(express.static('/public'));
    

hapi:
    
    
    const server = new Hapi.Server({
        port: 3000,
        routes: {
            files: {
                relativeTo: Path.join(__dirname, 'public')
            }
        }
    });
    
    await server.register(require('@hapi/inert'));
    
    server.route({
        method: 'GET',
        path: '/{param*}',
        handler: {
            directory: {
                path: '.'
            }
        }
    });
    

Now, you can access any static files by going to `localhost:3000/filename`. [inert](/module/inert) has many other options and capabilities. To see what all it can do, please see the [serving static files](/tutorials/servingfiles/?lang=en_US) tutorial.

##  Error Handling -> boom

hapi uses the [boom](/module/boom) module to handle errors. By default, boom will return the errors in JSON format. Express on the other hand will return a text response by default, which is suboptimal with a JSON API. Lets look a 404 error response with the default settings by submitting a `GET` request to `'/hello'`, which does not exists:

Express:
    
    
    Cannot GET /hello
    

hapi:
    
    
    {
        "statusCode": 404,
        "error": "Not Found",
        "message": "Not Found"
    }
    

###  Custom Messages

`boom` allows you to easily change the error message for any status code. Lets take the 404 error above and return a new message:

Express:
    
    
    res.status(400).send({status: 404, error: "Page not found"});
    

hapi:
    
    
    throw Boom.notFound('Page not found');
    

In Express, you set the status code, then send the error message body. In this case we return a JSON object with the status code and the error message. In `boom`, there is no need to return a JSON object with the status code, it does this by default. In the example above, you throw `Boom.notFound()` to set the error message. `boom` has a long list of 4xx and 5xx errors, such as `Boom.unauthorized()`, `Boom.badRequest()`, `Boom.badImplementation()`, etc. For a complete list, please see the [boom](/module/boom) documentation.

![clipboard](/img/clipboardCheck.png)