hapi request life cycle

After adding a few simple routes to your server, you will eventually come to the point where you need to add things like authentication, authorization, and have other use cases that need to be solved before your handler is ever reached. There are multiple approaches to this, such as creating a function to check authentication credentials, another for assigning authorization tokens that are called when every request is received initially by the handler. You could do this in the route prerequisites mentioned previously. However, if you forget to add these to a single route, or had something executed before your authentication function is called, you've left yourself open to secure data being accessible to unauthenticated users.

The approach which hapi uses to solve this is to have a well-defined request life cycle, with a reliable series of events that happen on every request. This gives you a fairly granular control over a request in an easy-to-extend and readable way.

The problem with the preceding approach of route or function ordering is that it is an implied life cycle, not a well-defined one, and is it is not always obvious where we are in the request life cycle. I see this with notes on modules such as express-validator, which must be called after body-parser or else the payload won't be validated, and no error will be thrown. This is a logic issue, not one related to code, and as such, there will be no errors for problems like this, which makes debugging a huge pain. I mentioned building infrastructure instead of application logic in the previous chapter, and this is a classic example of that.

With hapi, you don't have to worry about any of these things, because at all times you'll know where you are in the request life cycle. This is one feature that also makes it much easier to understand other codebases and the intention of the original developers when taking action on points within the request life cycle.

Let's look at the different stages in the request life cycle, bearing in mind that it is a lot to take in at once, and not necessary to remember. I often find myself returning to the API documentation to look up the event names to know when I want a certain action to take place, each time quite pleased that it was not my responsibility to implement, test, and document the life cycle I've built in each application. The full life cycle in the order of events can be found on hapijs.com at http://hapijs.com/api#request-lifecycle. I will cover it briefly here. So, when a request is received by a hapi server, it goes through the following events:

  • The onRequest extension point is called.

    Every request will call this extension point. This is before any authentication, payload parsing, and so on is done. This extension point is unique in that it occurs before any route matching has occurred. The request object is decorated with the request.setUrl() and request.setMethod() methods, which can be used for rewriting requests only at this extension point.

  • The next route matching occurs, based on request.path.
  • Query extensions are processed (for example, JSONP), and cookies are parsed.
  • The onPreAuth extension point is called.

    After the onPreAuth extension point, the request is authenticated. The payload is always parsed in this step so that payload authentication can also be performed.

  • The onPostAuth extension point is called.
  • Validation is then performed, first on the path parameters, the request query, and then the request payload.
  • The onPreHandler extension point is called.

    After the onPreHandler extension point is called, the route prerequisites will be evaluated, and then the route handler itself.

  • The onPostHandler extension point is called.

    Here request.response can be modified or a new response generated via a call to reply(). Usually, this would be to replace an error with an HTML page, like a 'not found' or internal server page.

  • Next, the response payload is validated.
  • The onPreResponse extension point is called.

    Again, as in the onPostHandler extension point, request.response can be modified or a new response generated via reply(). However, here the response will not go through the onPreResponse point to prevent an infinite loop.

  • The response is then sent to the client, with the response event emitted.
  • Finally, any tail actions are performed. tails are actions that the response sent to the client is not dependent on, and so may be performed after the response is sent. This could be something like updating a database, or adding audits to user actions in your application. You can read more about them in the API documentation at http://hapijs.com/api#requesttailname. When all tails are completed, a tail event is emitted.

Take note that some events are always called, and some are not. You may have not noticed the extension points throughout the life cycle; let's now look at how to add actions to extension points during different stages of the request life cycle.

Extending request life cycle events

Extending a request life cycle event has an easy-to-use API:

server.ext(event, method, [options])

So, if you wanted to log in to the console every time you received a request, it would be as follows:

…
server.ext('onRequest', function (request, reply) {
  console.log(`request received: ${request.}`);
  return reply.continue();
});
…

Note the function reply.continue() here—this is to return the control to the framework and indicate continuing with the request life cycle. The function reply() without continuing at this extension point would indicate that a response should be sent to the user from here, as it is likely there has been an error, and does not continue with the request life cycle. Not realizing this can be a common error when starting out with hapi. I will cover this in more detail when talking about the reply interface.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.246.223