typescriptintermediate

Node.js Graceful Shutdown Handler

Implement graceful shutdown to properly close connections and finish requests before exit.

typescript
import { createServer, IncomingMessage, ServerResponse } from 'node:http';

const connections = new Set<import('node:net').Socket>();
let isShuttingDown = false;

const server = createServer((req: IncomingMessage, res: ServerResponse) => {
  if (isShuttingDown) {
    res.writeHead(503, { 'Connection': 'close' });
    res.end('Server is shutting down');
    return;
  }
  // Normal request handling
  res.writeHead(200);
  res.end('OK');
});

// Track active connections
server.on('connection', (socket) => {
  connections.add(socket);
  socket.on('close', () => connections.delete(socket));
});

async function shutdown(signal: string) {
  if (isShuttingDown) return;
  isShuttingDown = true;
  console.log(`\n${signal} received. Starting graceful shutdown...`);

  // Stop accepting new connections
  server.close(() => {
    console.log('HTTP server closed');
  });

  // Close idle keep-alive connections
  for (const socket of connections) {
    socket.end();
  }

  // Cleanup resources
  try {
    // await database.disconnect();
    // await cache.quit();
    // await messageQueue.close();
    console.log('Resources cleaned up');
  } catch (err) {
    console.error('Cleanup error:', err);
  }

  // Force exit after timeout
  const forceTimeout = setTimeout(() => {
    console.error('Forced shutdown after timeout');
    process.exit(1);
  }, 10_000);
  forceTimeout.unref();
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err);
  shutdown('uncaughtException').then(() => process.exit(1));
});

server.listen(3000, () => console.log('Server running on :3000'));

Use Cases

  • Production Node.js server reliability
  • Zero-downtime deployment support
  • Proper resource cleanup on shutdown

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.