Created
November 24, 2025 18:27
-
-
Save hosh/3b494e47e8df7b1f0b23434a9c172d72 to your computer and use it in GitHub Desktop.
Nodejs shutdown handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 'use strict'; | |
| /* This module handles shutdown procedures to ensure a graceful shutdown | |
| - Function to register a shutdown callbacks | |
| - Call shutdown callbacks on uncaught promise rejections and signals | |
| - Call the exported setup() at the top of the bootup, usually in main.ts | |
| */ | |
| // import your logging code here | |
| const log = (level, message) => { | |
| // This is a log shim. Adapt it to your logger | |
| }; | |
| const error_types = ['unhandledRejection', 'uncaughtException']; // as NodeJS.UncaughtExceptionOrigin[]; | |
| const signal_traps = ['SIGTERM', 'SIGINT', 'SIGUSR2'] as NodeJS.Signals[]; | |
| const shutdown_handlers = []; | |
| // This is called to register a shutdown handler. Call this from elsewhere so that | |
| // cleanup code can be executed | |
| export const registerShutdownHandler = (f: () => Promise<void>): void => { | |
| shutdown_handlers.push(f); | |
| }; | |
| const triggerShutdownHandlers = async (): Promise<void> => { | |
| await Promise.all.forEach((x) => x() )); | |
| log("info", "All shutdown handlers have completed. Resuming exit"); | |
| }; | |
| export const shutdownNow = (reason: string, exit_code: number): Promise<void> => { | |
| try { | |
| log("info", `Receiving shutdown (reason: ${reason}). Starting shutdown.`); | |
| triggerShutdownHandlers(). | |
| then(() => { process.exit(exit_code) }). | |
| catch((err) => { | |
| log("error", `Error during shutdown: ${err}`); | |
| process.exit(127); | |
| }); | |
| } catch (_) { | |
| process.exit(128); | |
| } | |
| process.exit(exit_code); | |
| }; | |
| // Custom error handler, because this also works with unhandled rejections | |
| const handleErrors = (...args) => { | |
| const err = args[0]; | |
| try { | |
| log("warn", `Uncaught exception of unhandled rejection. Error (${err}) received. Starting graceful shutdown.`); | |
| triggerShutdownHandlers(). | |
| then(() => { process.exit(1) }). | |
| catch((err) => { | |
| log("error", `Error during shutdown: ${err}`); | |
| process.exit(2); | |
| }) | |
| } catch (_) { | |
| process.exit(1); | |
| } | |
| }; | |
| // Call this once during bootup, at the top of the bootup | |
| export const setup = () => { | |
| log("info", "Setting up shutdown handlers"); | |
| process.on("unhandledRejection", handleErrors); | |
| process.on("uncaughtException", handleErrors); | |
| signal_traps.forEach(type => { | |
| log("info", `Setting up signal trap ${type}`) | |
| process.once(type, (_signal) => { | |
| try { | |
| log("warn", `Signal ${type} received. Starting graceful shutdown`); | |
| triggerShutdownHandlers(). | |
| catch((err) => { | |
| log("error", `Error during shutdown: ${err}`); | |
| }). | |
| finally(() => { process.kill(process.pid, type) }) // fire off the OS signal again | |
| } catch (err) { | |
| log("error", `Error during shutdown: ${err}`); | |
| process.exit(2); // Return a different exit code to indicate an error during graceful shutdown | |
| } | |
| }) | |
| }); | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment