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)); | |
}); | |
}; |
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)); | |
} |
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); | |
}); | |
}; |
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
@thebergamo @hapijs @Alanhoff you know about https://t.co/9y3iqEBHFG ? :D
— Adri Van Houdt 🏳️🌈 (@AdriVanHoudt_) December 4, 2015
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