Developing your web app isn’t only about the happy path. You need to take care of proper error handling and that applies to more than just showing creative error messages for bad user inputs on forms.
Start the error handling journey with missing resources which is a common scenario: just append a random route path to your url.
This tutorial shows you how to set up a 404 route handler that can render a view template or reply JSON for REST requests.
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”
- How to Reply a JSON Response
- How to Set Response Status Code
- How to Handle 404 Responses for Missing Routes
- 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
Prepare Your Project
By default, hapi will properly respond with a 404 error message and status code for unavailable resources. If you’re building an app that renders web views, you want to keep the user in context and render a proper template as a 404 response as well. For REST requests, you’re better off by replying JSON.
If not already done, go ahead and install the following dependencies:
npm i -S vision boom
You’ll use vision to render and reply the 404 web view. With boom, you’ve an error utility which automatically sets the correct 404 response status code and creates a valid response payload.
In case you’re not familiar with view handling, read the tutorial on how to render views in hapi first.
The Default 404 Error Handling in hapi
There’s a default error handling built into hapi and it’s just a JSON message send to the browser as plain text. Well, that’s absolutely fine if you’re relying on JSON responses.
default 404 error message in hapi
For web applications that actually render views in the browser, the default error handling is not sufficient. Read on to know what steps you need to take to have a more appropriate setup for your web app.
Create a Better 404 Route Handler
To handle 404 errors in your hapi app, add a single route that represents a last resort. This route will match all route paths that didn’t fit to any other route in your server.
hapi v17
server.route({
method: [ 'GET', 'POST' ],
path: '/{any*}',
handler: (request, h) => {
const accept = request.headers.accept
if (accept && accept.match(/json/)) {
return Boom.notFound('Fuckity fuck, this resource isn’t available.')
}
return h.view('404').code(404)
}
})
hapi v16
server.route({
method: [ 'GET', 'POST' ],
path: '/{any*}',
handler: (request, reply) => {
const accept = request.raw.req.headers.accept
// take priority: check header if there’s a JSON REST request
if (accept && accept.match(/json/)) {
return reply(Boom.notFound('Fuckity fuck, this resource isn’t available.'))
}
reply.view('404').code(404)
}
})
The defined route handler will be applied to GET
and POST
requests. If you need to handle any other method, add it to the list.
The route’s path needs to be a path parameter which will also handle routes that contain multiple forward slashes. Routes like http://your.domain/not/available/route
will be caught as well and the any
path parameter will have the value of not/available/route
.
Within the route handler, differentiate between browser and REST requests. To reply requests that explicitly want JSON payload in response, you can use the regular expression to check whether the Accept
request header contains the value of json
.
Well, if you don’t need to handle any JSON requests, you can delete this part right away. Just keep in mind that this functionality can be added easily.
If there’s an accept
request header available and it also matches the value of json
, the response is handled with boom
. boom
will automatically create a proper JSON payload and set the HTTP status code to 404.
For requests from the browser, a view called 404
gets rendered and the status code 404 is explicitly set for the response.
That’s all the magic :) This route handler will catch all routes that don’t match any of your other routes within your hapi server. Because this route has the least restrictions for any paths and parameters, it’ll be called last by hapi.
Results: Web View and JSON Response
Of course the most interesting part is the result. The following screenshots illustrate the different responses depending on the request’s source.
404 web view response
For requests from the browser, you want to keep the user in your website context and render a nice looking web view. Your page might look more solid with a navigation bar and footer ;-)
404 JSON response
Requests from clients that want JSON in return should set the Accept
header to application/json
. In case of a 404 error, your handler will also keep the JSON format and respond properly with a JSON message containing the statusCode
, error
, and message
fields. Also, the HTTP status code is properly set to 404.
Sample Implementation
We’ve published a sample implementation showing you the 404 handling over at GitHub. The code includes a very basic hapi server with just two routes: an index page and the 404. Every route besides the index will be caught by the 404 handler.
In case you want to see the big picture, just head over to the sample project on GitHub.
Outlook
A proper 404 handling is something that doesn’t seem important in the first place. Actually, it’s absolutely fine to just have a very simple view or response handling for 404 errors. Just keep attention to how you’d feel if you’re visiting a route that isn’t available on a given website and there’s a techy, out of context error message presented as JSON.
Give your 404 page some love and take the opportunity to help users understand what happened and why this error occurs.
We’re happy to hear your thoughts and comments. Please don’t hesitate to use the comments below or find us on Twitter @futurestud_io. Let us know what you think!
Enjoy coding & make it rock!