Extension points in Hapi.js
Tags: hapi, node, whatilearnttoday
Published: 2020-02-19 05:24:52


Recently I started exploring Hapi.js . I read this concept called extension points, which sounded familiar to interceptors(kinda...). So, I thought of exploring them.

Each request in hapi follows a predefined path, the request lifecycle.
###Now, what is request lifecycle ?
check this out: https://hapi.dev/api/?v=19.1.1#request-lifecycle

### why extension points?
1. To create custom functionality along the lifecycle.
2. To let us know the precise order at which our application will run.

###Extension points

hapi has 7 extension points along the request lifecycle. And the order is
1. onRequest
2. onPreAuth
3. onCredentials
4. onPostAuth
5. onPreHandler
6. onPostHandler 
7. onPreResponse. 

To add a function to an extension point, you call 
```javascript
server.ext()
``` 
Lets look at an example:

I have created a route named "/employees" where I am using a dummy api to fetch a details of employees.

```javascript
'use strict';

const Hapi = require('@hapi/hapi'),
    Request = require('request-promise');

const init = async () => {
    const server = Hapi.server({
        port: 5000,
        host: 'localhost'
    })
    
    server.route({
        method: 'GET',
        path: '/employees',
        handler: function(req, h) {
            return Request('http://dummy.restapiexample.com/api/v1/employees', function (error, response, body) {
                console.log('response:', response);
            });
        }
    })

    server.ext('onRequest', function(request, h){
        console.log('inside onRequest');
        return h.continue;
    })
  
    await server.start();
    console.log('Server running at %s', server.info.port);
}

process.on('unhandledRejection', (err) => {
    console.log(err);
    process.exit(1);
});

init();
```

I am using nodemon to execute my server code
```javascript
nodemon server.js
```
When you hit this url http://localhost:5000/employees on your browser, you would notice in your console the message within our 'onRequest' extension point prints first and then our response. This made me feel that it looks like interceptor.

You might have noticed the statement
```javascript
return h.continue;
```
If you comment this piece of code , you will see this
```javascript
Error: onRequest extension methods must return an error, a takeover response, or a continue signal
```

In case of interceptors, we intercept the request and do some operation and then pass on the handler so that we can complete the request. The same goes here if you noticed the error statement.

The request path and method can be modified via the request.setUrl() and request.setMethod() methods.

For example:
```javascript
server.ext('onRequest', function(request, h){
        console.log('inside onRequest');
        request.setUrl('/');
        request.setMethod('POST');
        return h.continue;
    })
```
Here, we are setting the url to '/'. so, when we try to access http://localhost:5000/employees  we will be redirected to http://localhost:5000 but we must have '/' this route defined.
Next, you can change the request method from GET to POST using setMethod as we did above.

Earlier we talked about the order of lifecycle methods, so lets see when the methods are called.

```javascript
   server.ext('onRequest', function(request, h){
        console.log('inside onRequest');
        return h.continue;
    });

    server.ext('onPreAuth', function(request, h){
        console.log('inside onPreAuth');
        return h.continue;
    });

    server.ext('onCredentials', function(request, h){
        console.log('inside onCredentials');
        return h.continue;
    });

    server.ext('onPostAuth', function(request, h){
        console.log('inside onPostAuth');
        return h.continue;
    });

    server.ext('onPreHandler', function(request, h){
        console.log('inside onPreHandler');
        return h.continue;
    });

    server.ext('onPostHandler', function(request, h){
        console.log('inside onPostHandler');
        return h.continue;
    });

    server.ext('onPreResponse', function(request, h){
        console.log('inside onPreResponse');
        return h.continue;
    });

```
Here, we have added all the extension points in the order as mentioned the official documentation and have placed a log statement inside each extension point implementation.

Now, we need to access http://localhost:5000/employees to see what happens

If you check your console, you would see this order:

```javascript
Server running at 5000
inside onRequest
inside onPreAuth
inside onPostAuth
inside onPreHandler
response: IncomingMessage{
//entire response body
}
inside onPostHandler
inside onPreResponse
```
###Things to notice:
onPreAuth and onPostAuth are called regardless if authentication is performed.

But, onCredentials will be called only if authentication is performed and that is the reason we didn't see the log statement which we defined inside onCredentials.

#### I hope this helps you understand the flow of extension points and when to use them.

Thanks for reading this. Cheers !!!