This guide continues on extending your server’s functionality with the help of plugins. Within the previous tutorial, you’ve learned how to register a plugin to your hapi server instance and with that increase the servers feature set. Also, we’ve described hapi’s efficient plugin system and the unwritten best practice to split your application logic into various small, isolated modules.
Now it’s time to learn how to write your own plugin that adds your desired functionality to hapi. Due to the extensiveness of this hapi series, have a look at the series outline below to find other interesting guides.
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
Write a Plugin
Based on the fact that it’s recommended to extend your servers functionality using plugins, there should be a convenient way to write your own, custom one. Essentially, a plugin is an object with a register
property that returns a function with the signature function (server, options, next)
. Further, the register
function has an attributes
object that contains meta data about your plugin to provide some extra data for hapi. The attributes can be something like name
and version
.
var myplugin = {
register: function (server, options, next) {
// add functionality -> we’ll do that in the section below
// call next() to signal hapi that your plugin has done the job
next()
}
}
myplugin.register.attributes = {
name: 'myplugin',
version: '1.0.0'
}
module.exports = myplugin
We’ve mentioned that the register
function of your plugin takes three arguments server
, options
and next
. Let’s review each of them: server
is the reference instance to your server that you’re going to extend (like adding route handlers to render views or reply in other formats) or just use it to call server methods. The options
object contains plugin specific configuration that is required to run your plugin appropriately.
At last, you have to call next
to signal hapi that you’ve registered or configured the desired functionality and your plugin is done. You can also call asynchronous methods while registering your plugin. Just use the next
function at the right spot, like within the callback or promise.
Up to this point, you’ve seen the basics of how to write a custom plugin. Let’s make things more approachable and add a route handler within our custom plugin.
Add Functionality Within Your Custom Plugin
Within the getting started tutorial of this hapi series, we’ve created a basic hapi server that sends a Hello Future Studio!
message when calling the default /
route. The code below illustrates the mentioned server code:
var Hapi = require('hapi')
// create new server instance
var server = new Hapi.Server()
// add server’s connection information
server.connection({
host: 'localhost',
port: 3000
})
// add “hello world” route
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello Future Studio!')
}
})
// start your server
server.start(function (err) {
if (err) {
throw err
}
console.log('Server running at: ' + server.info.uri)
})
In the following, you’ll refactor the shown code above and create your own plugin that registers the route handler and responds with the Hello Future Studio!
message. Alright, let’s get going.
First, create a file called base-route.js
that will add a GET
route at path /
and replies with the given message.
base-route.js
var baseRoutes = {
register: function (server, options, next) {
// add “hello world” route
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello Future Studio!')
}
})
next()
}
}
baseRoutes.register.attributes = {
name: 'base-routes',
version: '1.0.0'
}
module.exports = baseRoutes
As you can see, we’re extending the server
instance by registering the route. Of course, you could enhance the route’s handler
method with additional functionality. For now, let’s just focus on the refactoring part and create your first plugin. Because the server.route()
method is a synchronous operation, we can add the next
call right after the route registration.
Within the servers.js
file, you need to remove the previously defined route registration and register the newly created plugin. If you need to recap on how to load plugins to your hapi server, have a look at the linked tutorial.
server.js
var Hapi = require('hapi')
// create new server instance
var server = new Hapi.Server()
// add server’s connection information
server.connection({
host: 'localhost',
port: 3000
})
// register plugins to server instance
server.register({
register: require('./base-route')
})
// start your server
server.start(function (err) {
if (err) {
throw err
}
console.log('Server running at: ' + server.info.uri)
})
Within the snippet above, you can see the call of server.register()
to add your plugin to the hapi server instance and with that, add the handler to respond any GET
requests on route path /
. We aren’t using any plugin options, that’s why we don’t pass them during the register phase.
To check if everything went smooth, start your server instance and open the browser or use curl to call localhost:3000
. You should receive the Hello Future Studio!
message.
Outlook
This tutorial walked you through the fundamentals on how to create your custom hapi plugin. You’ve also created your own plugin that registers a route handler to respond a request with a message. Use this basis and extend your plugins to provide the desired functionality and extend the server capabilities.
Within the next guide, you’ll get a grasp on how to render and serve beautiful views.
If you’ve any question, please let us know in the comments below or reach out at Twitter, @futurestud_io.
Enjoy coding & make it rock!