HTTP Shutdown
http-shutdown is a Node.js library designed for gracefully shutting down HTTP servers, addressing shortcomings of the native `server.close()` method. Unlike `server.close()` which only terminates the listening socket and waits for existing connections, `http-shutdown` actively closes idle keep-alive sockets and waits for all in-flight requests to complete before closing their associated sockets. This ensures no new requests are accepted and all current requests finish cleanly. The library, currently at version 1.2.2, appears to be in a maintenance phase with infrequent but stable releases. It offers two primary integration methods: explicitly wrapping an `http.Server` instance or extending the `http.Server` prototype with a `withShutdown()` method. It ships with TypeScript types, facilitating its use in modern TypeScript projects.
Common errors
-
`server.shutdown is not a function`
cause The `http.Server` instance was not properly augmented with `http-shutdown` functionality.fixEnsure you have either explicitly wrapped your server with `server = httpShutdown(server);` or called `require('http-shutdown').extend();` and subsequently `server = http.createServer(...).withShutdown();` before attempting to call `server.shutdown()`. -
`TypeError: Cannot read properties of undefined (reading 'shutdown')` (or similar related to `withShutdown`)
cause When using the `extend()` method, the `withShutdown()` method was not chained onto the `http.Server` instance, or `extend()` itself was not called globally.fixMake sure you call `require('http-shutdown').extend();` (or `httpShutdown.extend();` for ESM) once at the application's entry point, and then chain `.withShutdown()` directly after creating your server: `server = http.createServer(...).withShutdown();`. -
Server `close` event fires unexpectedly early, before all connections are handled by `http-shutdown`.
cause Relying on the native `server.on('close', ...)` event for post-shutdown cleanup logic can cause race conditions, as `server.close()` might be called internally before `http-shutdown` has finished closing all active and idle connections.fixAlways rely exclusively on the callback provided to `httpShutdown.shutdown(callback)` for knowing when the server has gracefully and completely shut down. Place all post-shutdown cleanup logic within this callback.
Warnings
- gotcha The library is primarily designed for `http.Server`. While it often works with `https.Server` due to underlying `net.Server` wrapping, ensure thorough testing for your specific HTTPS configuration.
- gotcha Mixing the `extend()` method with direct server wrapping (`server = httpShutdown(server)`) can lead to confusing behavior or conflicts.
- gotcha Calling `shutdown()` too early (e.g., before the server is fully listening) or multiple times on the same instance can lead to unexpected behavior or redundant operations.
- gotcha As a CommonJS-first library, direct ESM named imports might not work as expected without proper configuration or bundler handling.
Install
-
npm install http-shutdown -
yarn add http-shutdown -
pnpm add http-shutdown
Imports
- httpShutdown (function)
import { httpShutdown } from 'http-shutdown';import httpShutdown from 'http-shutdown';
- extend()
import { extend } from 'http-shutdown';import httpShutdown from 'http-shutdown'; httpShutdown.extend();
- WithShutdownServer (type)
import type { WithShutdownServer } from 'http-shutdown';
Quickstart
import * as http from 'http';
import httpShutdown, { WithShutdownServer } from 'http-shutdown';
// Create the http server
let server: http.Server | WithShutdownServer = http.createServer((req, res) => {
if (req.url === '/heavy-task') {
// Simulate a long-running request
console.log('Incoming heavy task request...');
setTimeout(() => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Heavy task completed!');
console.log('Heavy task finished.');
}, 2500);
} else {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from http-shutdown!');
}
});
// Wrap the server object with additional functionality.
server = httpShutdown(server);
const PORT = 3000;
// Listen on a port and start taking requests.
server.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
console.log('Try accessing / and /heavy-task. Server will attempt graceful shutdown in 7 seconds...');
// Sometime later... initiate graceful shutdown.
setTimeout(() => {
console.log('Initiating graceful shutdown...');
(server as WithShutdownServer).shutdown((err) => {
if (err) {
console.error('Shutdown failed:', err.message);
process.exit(1);
return;
}
console.log('Everything is cleanly shutdown.');
process.exit(0);
});
}, 7000);
});
// Handle common process termination signals for robust shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, initiating shutdown.');
(server as WithShutdownServer).shutdown((err) => {
if (err) {
console.error('Shutdown failed on SIGTERM:', err.message);
process.exit(1);
return;
}
console.log('Everything is cleanly shutdown after SIGTERM.');
process.exit(0);
});
});
process.on('SIGINT', () => {
console.log('SIGINT received, initiating shutdown.');
(server as WithShutdownServer).shutdown((err) => {
if (err) {
console.error('Shutdown failed on SIGINT:', err.message);
process.exit(1);
return;
}
console.log('Everything is cleanly shutdown after SIGINT.');
process.exit(0);
});
});