Migrating an existing hapi app to hapi v17 might reveal errors that you didn’t see before. This tutorial focuses on errors that focus on lifecycle methods where you must return an error, a takeover response, or a continue signal
.
If you ran into this issue, let’s fix it!
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
Explicitly Return in hapi V17
The shift to async/await in hapi v17 requires you to return values from lifecycle methods. You don’t have the reply
callback anymore. Tell hapi how to handle requests by return
ing individual values from lifecycle methods, like route handlers.
Lifecycle Methods Called Before the Handler
Lifecycle methods are an essential part of the hapi framework. They provide you the request
and h
response toolkit and optionally an error
object. You also know lifecycle methods from extension points or the failAction
method for validations.
Let’s use the failAction
as an example. You might want to render the related view of your route handler, but with error details to point out issues to the user.
If you return from the failAction
method, tell hapi to send your response from failAction
right away without touching the route handler. Do this by chaining .takeover()
on your custom response that you send with the response toolkit h
.
failAction: (request, h, error) => {
const email = request.payload.email
const errors = ['your', 'list', 'of', 'errors', ':)']
return h
.view('join', {
email,
errors
})
.code(400)
.takeover() // <-- this is the important line
}
You can surely use any kind of response, like JSON (h.response(json).code(400).takeover()
) or an HTML view (see code snippet) with a custom HTTP status code.
onRequest Extension Methods
You can extend hapi’s request lifecycle with a dedicated lifecycle method. Depending on the extension point, hapi calls it before a route handler.
A special case for this is the onRequest
extension point. Returning from that needs to be a takeover response (by using h.takeover()
), an error or the continue signal.
The code snippet below illustrates a redirect for URL paths having a trailing slash to the URL pendant without the trailing slash.
const _ = require('lodash')
server.ext('onRequest', (request, h) => {
if (_.endsWith(request.url.path, '/')) {
// redirect to the url without slash
const url = _.trimEnd(request.url.path, '/')
return h
.redirect(url)
.code(301)
.takeover() // <-- this is the important line
}
// continue requests without trailing slash
return h.continue
})
Tell hapi to send your custom response from onRequest
and do not proceed the actual request lifecycle.
In contrast, requests without a trailing slash on the URL path proceed the request lifecycle.
Summary
Hapi is flexible when it comes to sending responses from different parts of the request lifecycle. Use the h.takeover()
and tell the framework to send the response without further processing of other lifecycle steps.