Actively maintained applications get started and stopped very often. It’s not like the operating system of your web server that doesn’t get touched for years once set up ;-) Especially during development, you’re usually starting a local instance of your app multiple times a day!
Stopping a local instance of your app doesn’t require a lot of attention. Just kill the process and you’re done. In production, you want to avoid downtime and certainly data loss. No interruptions for the user!
This guide shows you how to correctly finish and close existing connections and afterwards shut down the hapi server.
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
- How to Run Separate Frontend and Backend Servers Within a Single Project
- How to Use Server Labels
- How to Correctly Stop Your Server and Close Existing Connections
- 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
Correctly Stop Your hapi Server
Depending on your application setup, you might want to gracefully stop your hapi server and close existing requests, background processing, database interactions, and so on before stopping the actual server process. Precisely, you want to do the actual application shutdown first and ultimately stop the server once everything else has finished.
To correctly tear down the hapi server, you should listen on the SIGINT
signal which is emitted by pressing CRTL + C
on your keyboard or when stopping the process using your process manager or init system. At the moment you receive a SIGINT
in your application you can use hapi’s server.stop()
method which waits for open connections to be processed and shuts down afterwards.
The following code snippet illustrates a simple example of starting a hapi server and waiting for the signal to stop the process.
hapi v17
const Hapi = require('hapi')
const server = new Hapi.Server({
host: 'localhost',
port: 3000,
})
async function start () {
// start your server
try {
await server.start()
} catch (err) {
console.error(err)
process.exit(1)
}
console.log('Server running at: ', server.info.uri)
}
start()
// listen on SIGINT signal and gracefully stop the server
process.on('SIGINT', function () {
console.log('stopping hapi server')
server.stop({ timeout: 10000 }).then(function (err) {
console.log('hapi server stopped')
process.exit((err) ? 1 : 0)
})
})
hapi v16
const hapi = require('hapi')
const server = new hapi.Server()
server.connection({
host: 'localhost',
port: 3000,
})
server.start((err) => {
if (err) {
throw err
}
console.log('Server running at:', server.info.uri)
})
// listen on SIGINT signal and gracefully stop the server
process.on('SIGINT', function () {
console.log('stopping hapi server')
server.stop({ timeout: 10000 }).then(function (err) {
console.log('hapi server stopped')
process.exit((err) ? 1 : 0)
})
})
The interesting part is the listener process.on('SIGINT', …)
and its function body. Once the SIGINT
is received, call hapi’s server.stop()
method to reject incoming requests and just finish up existing ones.
The stop
method accepts an options
object and an optional error-only callback (function (error)
) . If you skip the callback parameter on server.stop()
, a promise is returned (as we use within the example above).
Available Options
timeout
: milliseconds, default = 5000 (= 5 seconds). Timeout before forcefully stopping the server
timeout
is the only option available to customize hapi’s shutdown procedure. That’s the time interval hapi waits until all connections should be closed. Once the time is up, hapi forcefully closes all connections.
Timeout on Single Instances?
As described above, once you initiate the server stop all existing connections get closed and hapi waits at most the defined timeout interval to finish running tasks. Once the timeout is up, the callback/promise returns and still existing processings get interrupted.
Let’s say you want to restart your single hapi server instance with a considerable timeout of 30 seconds and there’s a client that requires a lot of processing on server side. Once you submitted the restart
command, hapi will wait for at most 30 seconds to finish the processing and close the connection. Other requests within these 30 seconds interval will be rejected and need to wait or request again to be accepted properly.
Outlook
Stopping your hapi server with grace is very important in production environments. Keep service interruptions down to a minimum und most importantly don’t lose user data due to headless server stops. Hapi provides the functionality to gracefully stop the server process and allows you to customize the waiting time before the process is teared down.
In the next tutorial, you’ll learn how to avoid service interruptions in your hapi app and benefit from zero downtime restarts with the help of PM2.
We appreciate your feedback and love to help you if there’s a question or bug that doesn’t let you sleep. Let us know in the comments or on twitter @futurestud_io.
Make it rock & enjoy coding!