How to use Hapi decorate

2015-12-04
14 min read

Brief

Days ago, I want to improve performance to my boilerplate for Hapi + MongoDB apps. On this crusade, I came across an interesting feature in the Hapi documentation: Decorate.

This open my mind about how handle my throws in the source of my Hapi apps, and I want to share with you!

Previously

Previously I meet Decorate my implementation in controllers/xpto is something like this:

// [GET] /user/{id}
UserController.prototype.get = function (request, reply) {
let id = request.params.id;
this.model.findOneAsync({_id: id})
.then((user) => {
if (!user) {
reply(Boom.notFound());
return;
}
reply(user);
})
.catch((err) => {
reply(Boom.badImplementation(err.message));
});
};
view raw user.js hosted with ❤ by GitHub

Old controllers/user.js without decorate

This is an extracted part of my controllers/user.js, the method GET will return the information about an single user.

But you can see in this case I need to import Boom and reply then every time I need to dispatch an error for User not found. And in every new controller import Boom again.

You can just make boom globally, and then just call where you want importing Boom once! — Some newbie Dev

Decorate to the rescue!

'use strict';
const Boom = require('boom');
exports.register = register;
exports.register.attributes = {
name: 'reply-decorates',
version: '1.0.0'
};
function register (server, options, next) {
// create yours decorates
server.decorate('reply', 'notFound', notFound);
server.decorate('reply', 'badImplementation', badImpl);
server.decorate('reply', 'unauthorized', unauthorized);
next();
};
function notFound (messsage) {
return this.response(Boom.notFound(message));
}
function badImpl (messsage) {
return this.response(Boom.badImplementation(message));
}
function unauthorized (messsage) {
return this.response(Boom.unauthorized(message));
}
view raw decorate.js hosted with ❤ by GitHub

lib/decorate.js plugin

As you can see above, I write my common errors in a file and export a Hapi's Plugin. We can just load this plugin in your server instance and call it on yours controllers like in this snippet:

// [GET] /user/{id}
UserController.prototype.get = function (request, reply) {
let id = request.params.id;
this.model.findOneAsync({_id: id})
.then((user) => {
if (!user) {
return reply.notFound('User not found for the id: '+id);
}
reply(user);
})
.catch((err) => {
reply.badImplementation(err.message);
});
};
view raw user.js hosted with ❤ by GitHub

New controllers/user.js with decorate

Just cleaner right?

More than you saw here

Decorates can do more than just implement a simple wrapper for errors in your controller. You can extend the request, reply and server interfaces just like you saw above.

Importing and load your plugin in one place and sharing it in your entire application with this simple function Decorate.

You can see more about this in Hapi Documentation here.

Conclusion

This awesome feature of Hapi is very helpful if you want to write less code and keep DRY in your mind for bind your focus on your Business.

This and another studies are based on my Boilerplate called Start Hapiness, if you have any suggestion for improve this project feel free to open an issue or a pull request, it is both much welcomed!


Updates

2015–12–04 23:12:00

My example about using decorators to handle on time Boom module in reply interface has a module called hapi-boom-decorators. Thanks Adri Van Houdt for this update =D