With the release of hapi 9, the developers stripped down hapi’s internal dependencies and removed the previously integrated vision plugin. This plugin was responsible to decorate hapi’s server
object with the views
method. Removing the plugin from the framework’s core resulted in losing the server.views
method. Don’t worry, this breaking change doesn’t require much effort to get server.views
back in hapi business.
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
Views in Hapi 8 and Lower
Until hapi 8, you can define your template engine by directly passing an options object to server.views
. The code below illustrates a hapi server configuration by setting handlebars as the template engine:
const hapi = require('hapi')
// Create hapi server instance
const server = new hapi.Server()
// add connection parameters
server.connection({
host: 'localhost',
port: 3000
});
server.views({
engines: {
html: require('handlebars')
}
});
// configure routes, etc
// …
// Start the server
server.start(function() {
console.log('Server started at: ' + server.info.uri);
});
Since hapi shipped with the vision
plugin integrated by default, you could directly access server.views
and pass the configuration object to customize the template handling for your project.
This method doesn’t work anymore in hapi 9 and higher. The section below describes how to get the view configuration back into hapi.
Views in Hapi 9 and Higher
In hapi 9, the vision
plugin was removed from the framework’s direct dependencies and therefore hapi “lost” the ability to render views with various template engines. To get the server.views
method in hapi, you need to manually add the vision
and inert
plugins and register them with hapi. Before we dive into the framework’s configuration, let’s shortly review the depdendencies
section of your package.json
file.
Required depdencies for hapi 9 and higher (defined in pacakge.json
)
"dependencies": {
"hapi": "^9.3.1",
"inert": "^3.0.1",
"vision": "^3.0.0"
}
The snippet above only adds the current stable versions of hapi
framework, inert
and vision
plugins as dependencies. The vision plugin is required to add template rendering support to hapi. inert handles static files and directory support for hapi. This static file and directory support is required if you want to deliver static asset files like JS, CSS or images by just defining the path to the respective directory. Hapi — precisely inert
— will handle everything for you!
The following code illustrates how to configure view support in hapi 9 and higher:
hapi v17
const Hapi = require('hapi')
// Create hapi server instance
const server = new Hapi.Server({
host: 'localhost',
port: 3000
})
async function liftOff() {
await server.register([
{
plugin: require('vision') // add template rendering support in hapi
},
{
plugin: require('inert') // handle static files and directories in hapi
}
])
server.views({
engines: {
html: require('handlebars')
}
})
try {
await server.start()
console.log('Server started at: ' + server.info.uri);
} catch (err) {
process.exit(1)
}
}
liftOff()
hapi v16 (till v9)
const hapi = require('hapi')
// Create hapi server instance
const server = new hapi.Server()
// add connection parameters
server.connection({
host: 'localhost',
port: 3000
});
server.register([
{
register: require('vision') // add template rendering support in hapi
},
{
register: require('inert') // handle static files and directories in hapi
}
], function(err) {
if (err) {
throw err;
}
// set view configuration in plugin register callback
server.views({
engines: {
html: require('handlebars')
}
});
});
// configure routes, etc
// …
// Start the server
server.start(function() {
console.log('Server started at: ' + server.info.uri);
});
First, we need to register the vision
and inert
plugins to hapi. You can do this by passing an array of objects to hapi’s server.register
function. Each object within the array requires at least register
and optionally options
properties to be defined. The register
property is the plugin import and options
is an individual options object to customize the plugin configuration. The example above doesn’t make use of options
.
The second step is to define the view configuration. For that, use the callback function of the server.register
method. Within the callback, you can finally access server.views
which enables you the custom view configuration. Pass your individual options to the method and that’s it. Custom view support is back in hapi!
Addional Resources
- Tutorial on hapi views with configuration options like engines, paths, etc.
- How to Create a Dynamic Handlebars Layout Template in Hapi
- hapi 9 Release Notes
- vision plugin to add template rendering support to hapi
- inert plugin to add support for static file and directory handling