The reply interface

In the various life cycle events in hapi, such as authentication, route handlers, route prerequisites, you will see reply as one of the function arguments. This is the reply interface.

Throughout this book and the API documentation, you will see the function reply in two forms—either as reply() or as reply.continue()—which have two different purposes. reply.continue() is generally used to return the control flow back to the framework and continue through the request life cycle, whereas reply() is generally used to generate the response to be returned to the client. The only exception to this is inside the route prerequisites where reply() is used, as it assigns a variable to the request.pre object.

reply() can accept two parameters (error and response), but this is rarely used, as reply always acts on the first defined parameter that it is passed in. For example, if reply() is passed two variables and the first one is not null, it treats this as an error reply. If only one passed parameter is an error, it will do the same. If one non-error parameter is passed or there is a null or undefined parameter and a non-error parameter, it will consider it a normal response. To summarize:

// error with 500 status code
return reply(new Error());

// error with 500 status code
return reply(new Error(), 'success!');

// returns 'success!' with 200 status code
return reply(null, 'success!');

// returns 'success!' with 200 status code
return reply('success!');

If an error object is passed to reply(), it will wrap it in a boom error object.

Note

boom is hapi's very handy error handling library. It provides a set of utilities for returning HTTP friendly errors. You can read more on boom on GitHub at https://github.com/hapijs/boom.

In fact, there are a various types to which reply() can be passed, which are very helpful when it comes to how you choose to handle your control flow (from hapijs.com):

  • Null
  • Undefined
  • String
  • Number
  • Boolean
  • Buffer object
  • Error object
  • Stream object (any Stream object must be compatible with the streams2 API and not be in objectMode)
  • Promise object
  • Any other object or array

Whatever type you pass to reply(), hapi will figure out what it needs to send in the response, such as payload, content-type, and status codes. There are examples of routes replying with all these types in the source code supplied with this book.

It is worth knowing that in a route handler when reply() is called, the response will not be sent to the client until the process.nextTick() callback.

Note

If you are unfamiliar with node's event loop and the process.nextTick() API, I suggest you watch this video by Philip Roberts, which provides a great explanation of the same: https://www.youtube.com/watch?v=8aGhZQkoFbQ.

Waiting on process.nextTick() enables you to make changes to the returned response before it is sent, improving code readability and simplifying route handler logic. This is useful when you want to specify the status code or content type instead of letting hapi figure it out.

This is common in catch all routes where you might want to return a special 'not found' page for requests that haven't matched another route. This would look something like the following:

…
server.route({
  method: '*',                                          // [1]
  path: '/{p*}              ',                          // [2]
  handler: function (request, reply) {
    return reply('The page was not found').code(404);   // [3]
  }
});
…

This is a good example to explore in detail, as it puts some of the concepts we discussed earlier in the chapter together. With reference to the line numbers given in the preceding code, let's go through the concepts:

  • [1]: The HTTP method for this route. * is the wildcard, which means it can be any HTTP method.
  • [2]: The path. here we used {p*}, where * is a wild card. This means that there can be zero or more path segments so that any combination of method and path will match this route. If you think back to the hapi routing algorithm, this is the most generic route definition possible, so will always be ordered last in the sorted routing table.
  • [3]: The reply() function, where we alter the response before it is sent. Without the .code() at the end of the reply here, this route would return a plaintext response of 'The page was not found' with a status code of 200, which wouldn't really make sense. We could pass in a Boom.notFound() error object, or simply hard code the status code like we did here. The other things we can modify here are:
    • The content type:
      return reply('The page was not found').type('text/plain');
    • The headers:
      return reply(''The page was not found').header('X-Custom', 'value');

Along with being able to modify responses before they are sent, we can modify the reply interface itself to provide custom functions for reducing boilerplate and make for more readable code. Let's look at the different ways this is commonly used in the next section.

Custom handlers

Now that we have some idea of what is happening under the hood before a request reaches our handler, and what happens after we call reply(), let's look at the different methods that hapi has for dealing with common use cases like static content. First it's good to know that it's actually possible to extend some of the interfaces in hapi like server and reply to get custom actions, for example:

…
const hello = function (name) {
  return this.response({ hello: name });
}
server.decorate('reply', 'hello', hello);
server.route({
  method: 'GET',
  path: '/{name}',
  handler: function (request, reply) {
    return reply.hello(request.params.name);
  }
});
…

The preceding code creates a new custom reply method called hello, which we can then call in our handlers. We can also do something similar with server.handler(), where we pass a different configuration object; this would look like the following:

…
// Defines new handler for routes on this server
server.handler('hello', (route, options) => {
  return function (request, reply) {
    const hello = options.customHello || 'Hello';
    const name = request.params.name;
    return reply(`${hello} ${name}`);
  }
});

server.route({
  method: 'GET',
  path: '/{name}',
  handler: {
    hello: {
      customHello: 'Welcome'
    }
  }
});
…

Fortunately, hapi provides plugins that, when registered, provide a range of custom reply methods and handlers for things like serving static content, proxies, and templating among many others. Let's look at the first of these for serving static file content: inert.

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

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