Within last week’s tutorial, you’ve learned how to get your first hapi server up and running. This guide will continue on that basis and you’ll extend the server’s functionality by adding a plugin that enhances log output. Actually, it’s important to understand hapi’s powerful plugin system, because it allows you to break your application down into smaller, independent modules. However, we’ll get to that in a second.
Before moving on, have a look at other posts within this series, there might be a good catch.
hapi Series Overview
- What You’ll Build
- Prepare Your Project: Stack & Structure
- Environment Variables and Storing Secrets
- Set Up MongoDB and Connect With Mongoose
- Sending Emails in Node.js
- Load the User’s Profile Picture From Gravatar Using Virtuals in Mongoose
- Implement a User Profile Editing Screen
- Generate a Username in Mongoose Middleware
- Displaying Seasons and Episodes for TV Shows with Mongoose Relationship Population
- Implementing Pagination for Movies
- Implement a Watchlist
- Create a Full Text Search with MongoDB
- Create a REST API with JSON Endpoints
- Update Mongoose Models for JSON Responses
- API Pagination for TV Shows
- Customize API Endpoints with Query Parameters
- Always Throw and Handle API Validation Errors
- Advanced API Validation With Custom Errors
- Create an API Documentation with Swagger
- Customize Your Swagger API Documentation URL
- Describe Endpoint Details in Your Swagger API Documentation
- 10 Tips on API Testing With Postman
- JWT Authentication in Swagger API Documentation
- API Versioning with Request Headers
- API Login With Username and Password to Generate a JWT
- JWT Authentication and Private API Endpoints
- Refresh Tokens With JWT Authentication
- Create a JWT Utility
- JWT Refresh Token for Multiple Devices
- Check Refresh Token in Authentication Strategy
- Rate Limit Your Refresh Token API Endpoint
- How to Revoke a JWT
- Invalidate JWTs With Blacklists
- JWT Logout (Part 1/2)
- JWT “Immediate” Logout (Part 2/2)
- A Better Place to Invalidate Tokens
- How to Switch the JWT Signing Algorithm
- Roll Your Own Refresh Token Authentication Scheme
- JWT Claims 101
- Use JWT With Asymmetric Signatures (RS256 & Co.)
- Encrypt the JWT Payload (The Simple Way)
- Increase JWT Security Beyond the Signature
- Unsigned JSON Web Tokens (Unsecured JWS)
- JWK and JWKS Overview
- Provide a JWKS API Endpoint
- Create a JWK from a Shared Secret
- JWT Verification via JWKS API Endpoint
- What is JOSE in JWT
- Encrypt a JWT (the JWE Way)
- Authenticate Encrypted JWTs (JWE)
- Encrypted and Signed JWT (Nested JWT)
- Bringing Back JWT Decoding and Authentication
- Bringing Back JWT Claims in the JWT Payload
- Basic Authentication With Username and Password
- Authentication and Remember Me Using Cookies
- How to Set a Default Authentication Strategy
- Define Multiple Authentication Strategies for a Route
- Restrict User Access With Scopes
- Show „Insufficient Scope“ View for Routes With Restricted Access
- Access Restriction With Dynamic and Advanced Scopes
- hapi - How to Fix „unknown authentication strategy“
- Authenticate with GitHub And Remember the Login
- Authenticate with GitLab And Remember the User
- How to Combine Bell With Another Authentication Strategy
- Custom OAuth Bell Strategy to Connect With any Server
- Redirect to Previous Page After Login
- How to Implement a Complete Sign Up Flow With Email and Password
- How to Implement a Complete Login Flow
- Implement a Password-Reset Flow
- Views in hapi 9 (and above)
- How to Render and Reply Views
- How to Reply and Render Pug Views (Using Pug 2.0)
- How to Create a Dynamic Handlebars Layout Template
- Create and Use Handlebars Partial Views
- Create and Use Custom Handlebars Helpers
- Specify a Different Handlebars Layout for a Specific View
- How to Create Jade-Like Layout Blocks in Handlebars
- Use Vue.js Mustache Tags in Handlebars Templates
- How to Use Multiple Handlebars Layouts
- How to Access and Handle Request Payload
- Access Request Headers
- How to Manage Cookies and HTTP States Across Requests
- Detect and Get the Client IP Address
- How to Upload Files
- Quick Access to Logged In User in Route Handlers
- How to Fix “handler method did not return a value, a promise, or throw an error”
- How to Fix “X must return an error, a takeover response, or a continue signal”
- Query Parameter Validation With Joi
- Path Parameter Validation With Joi
- Request Payload Validation With Joi
- Validate Query and Path Parameters, Payload and Headers All at Once on Your Routes
- Validate Request Headers With Joi
- Reply Custom View for Failed Validations
- Handle Failed Validations and Show Errors Details at Inputs
- How to Fix AssertionError, Cannot validate HEAD or GET requests
Make Use of hapi’s Plugin System
hapi ships with an extensive and powerful plugin system right away. Actually, it’s an unwritten best practice to break your application into isolated chunks of functionality and reusable utilities. Optimally, you can break down your application into individual plugins and the hapi server instances just provides the required configuration.
Add Console Output as a Plugin
Process monitoring can be an essential part of your application to listen on server events to identify the data flow within your application and individual states. Further, it can be used to provide proper console logs. There’s a process monitoring plugin for hapi, called good, and you’ll use it to listen in server events. Additionally, you’ll use a reporter plugin —good-console— to output data for selected events to the console.
Starting Point
As a basis for the upcoming examples, create a simple hapi server listening on localhost
with port 3000
.
var Hapi = require('hapi')
// create new server instance
var server = new Hapi.Server()
// add server’s connection information
server.connection({
host: 'localhost',
port: 3000
})
That’s the starting point and you’ll extend that server by adding a plugin as described in the following paragraph.
Load a Plugin to Your Server Instance
The hapi server instance provides a register(server, options, next)
method that excepts either a single plugin or a list of plugins. The method signature for register()
outlines the server
instance itself and as a second parameter, an options
object. Of course, each plugin requires its own options and has a different schema. Just have a look at the plugin’s documentation to know what options you need to provide.
Generally, you can easily add a plugin like this:
// shortcut to register a plugin to hapi’s server instance
server.register(require('my-plugin'))
Further, you’ve the ability to add a callback function as the last parameter for the server.register()
method. However, if your plugin doesn’t require any options, you can just pass it to hapi’s server and extend its default functionality.
In a later tutorial, we’ll go in detail on how to create your own custom plugin. For now, we’ll use the existing good plugin from hapi’s ecosystem and register it to the server. Besides good, we’ll define good-console as a reporter within the good plugin’s options.
The snippet above showed you how to easily register a plugin to your hapi server instance. There are situations where your plugin needs application specific configuration or options and you want them to pass during the plugin registration phase. Actually, that’s also available using the register()
method by passing an object that provides two fields: register
and options
.
The following code block illustrates the registration of the good plugin within the server.register({…})
method.
var Good = require('good')
// register plugins to server instance
server.register({
register: Good,
options: {
reporters: [{
reporter: require('good-console'),
events: {
response: '*',
log: '*'
}
}]
}
})
As you can see, if you want to pass further options to your plugin, use the method shown in the code above. The good plugin requires and receives further options and we define good-console as the console output for the server events of response
and log
. That means, using the server.log
method will trigger the log
event and good outputs the provided data to the command line.
At this point, you may ask yourself: it’s recommended to break down the application code into plugins, is there a way to register multiple plugins at once to avoid having lots of server.register
statements? In short: yep, hapi got your back!
Load Multiple Plugins at the Same Time to Your Server
Actually, hapi allows you to load either one or many plugins at the same time using the register()
method. Of course, you can use the ways as already described above to add a plugin. the following code block outlines the three ways to import plugins into hapi’s server instance.
var Good = require('good')
// register multiple plugins at once to server instance
server.register([
{
register: require('good'),
options: {
reporters: [ {
reporter: require('good-console'),
events: { log: '*', response: '*' }
} ]
}
},
// register plugin without options #1
{
register: require('my-plugin')
},
// register plugin without options #2
require('another-plugin')
])
Please note that you need to provide a list of plugins for the server.register()
method to add multiple at the same time. Within the list, you can either define an object having at least the register
field defined and optionally providing options
. Further, you can just pass the plain plugin by requiring it right away.
Impact of Good Plugin to the Server
Up to this point, you’ve learned how to register the good plugin to your server. Let’s explore the changes when applying the plugin for use. The example code for this tutorial is available on GitHub and we recommend to check it out on your own.
In the following, you’ll see the difference between the console outputs with and without using the good plugin.
Before
// start hapi server
server.start(function (err) {
console.log('Server running at: ' + server.info.uri)
})
Console output:
$ node server.js
Server running at: http://localhost:3000
After
// start hapi server
server.start(function (err) {
server.log('info', 'Server running at: ' + server.info.uri)
})
Console output:
$ node server.js
160411/102456.564, [log,info], data: Server running at: http://localhost:3000
Leveraging good allows you to keep track of fine grained information and especially the output importance. Using just console.log(msg)
won’t put any priority to the information, but using server.log('info', msg)
or server.log('error', msg)
indicates directly the difference between each of the log messages.
That’s just the tip of the logging iceberg and actually it’s not the priority of this post. The important take away is to understand how you register plugins to hapi.
Outlook
This tutorial guided you through the required knowledge to import existing or custom plugins. You’ve learned how to register one or many plugins to your hapi server instance and besides that, how to pass plugin specific options during registration phase.
We’ve mentioned at the beginning of this post, that it’s best practice to create custom plugins for the functionality of your application. Within the next post, you’ll learn how to create your own custom plugin to extend the server’s functionality.
If you feel there’s anything missing in this guide, please let us know in the comments below or send us a tweet @futurestud_io
Enjoy coding & make it rock!
Additional Resources
- Code examples for this hapi series on GitHub
- good process monitoring for hapi
- good-console, a reporter for good to output data to the console