mrkeyoor.com_
Sat 08 Aug 20:59 UTC
npmWeb Backendupdated 08 Aug 2026

stoppable

stoppable is a dependency-free CommonJS decorator for Node HTTP and HTTPS servers. It adds server.stop(callback), tracks how many requests are active on each socket, closes idle keep-alive sockets during shutdown, lets active responses finish, and can destroy whatever remains after a configured grace period. The callback reports both an error and whether shutdown stayed graceful. It was created to fill an old server.close behavior gap; on Node 19 and newer, core server.close already closes idle HTTP connections.

Verdict

Do not add stoppable merely to make server.close reap keep-alives on current Node; core fixed that problem. Keep it only for an older service or a callback-based grace deadline you have tested, and plan an explicit path for WebSockets and every non-HTTP resource.

API stability4/5The version 1 API is tiny and has not moved: decorate an HTTP or HTTPS server, choose a grace value, and call stop(callback). Its behavior is easy to inspect in one source file. The lost point is for changing the server object with undocumented-looking _pendingSockets state and a nonstandard stop method, plus a two-argument callback that TypeScript cannot verify because the package publishes no declarations.
Docs3/5The README explains the constructor, Infinity default, immediate force-close at grace 0, callback graceful flag, connection bookkeeping, FIN-first design, and old benchmark. It does not address Node's newer closeIdleConnections and closeAllConnections APIs, the Node 19 server.close change, repeat stop calls, WebSocket upgrades, HTTP/2, promise usage, signal orchestration, or TypeScript. The linked motivation is therefore historically useful but no longer a current install argument.
Maintenance2/5Version 1.1.0 was published November 10, 2018, the repository's last push was October 31, 2023, and GitHub reports 28 open issues and pull requests. The repository is not archived and a small dependency-free module can remain usable for years, but Node core has changed the shutdown behavior that originally justified it. Lack of a release responding to that platform change is material maintenance risk.
Ecosystem3/5The package recorded 5,086,284 downloads for the fetched week, has no runtime dependencies, and works with the standard Node HTTP and HTTPS server objects used beneath frameworks such as Express. It has only 404 GitHub stars, no included types, no ESM entry, and no integration for health checks, WebSockets, HTTP/2, process signals, or resource cleanup. Much of the continuing volume is likely transitive legacy use rather than new adoption.

Use it if

  • You maintain an older Node HTTP or HTTPS service where server.close leaves idle keep-alive sockets open
  • You want one tiny CommonJS helper that waits for active requests and then forces remaining sockets after a fixed grace period
  • Your shutdown code uses callbacks and needs a boolean telling it whether the grace deadline forced termination
Skip it if

Setup reality

Install stoppable and require it from CommonJS; the package has no ESM export map and ships no TypeScript declarations. It accepts an existing http.Server or https.Server and returns that same object after adding stop, so you can decorate inline or after construction. The second constructor argument is the grace period in milliseconds. Its default is Infinity, which means a stuck request can keep shutdown waiting forever; production code should almost always choose a finite deadline. A value of 0 ends and then destroys all tracked sockets immediately, including active work. stop() schedules its work with setImmediate, calls server.close to stop new connections, sends FIN to idle sockets, and lets each active response finish before ending that socket. When the deadline fires it ends every tracked socket and destroys them on the next turn. The callback receives (error, gracefully), not the usual one-argument Node callback. Signal handling is still your job, including ignoring repeated SIGTERM or SIGINT while shutdown is already underway, stopping background consumers, closing database pools, and choosing the final process exit code. The package tracks only HTTP and HTTPS request lifecycles. WebSocket upgrades, HTTP/2 streams, raw TCP servers, open database handles, timers, and jobs need separate shutdown logic. Modern Node changes the decision: server.close has reaped idle HTTP connections since Node 19, and Node 18.2 added closeIdleConnections and closeAllConnections. If your supported runtime is current, a small local shutdown function using those core methods is easier to type, audit, and adapt. If you retain stoppable, test slow responses, keep-alive clients, aborted requests, forced deadlines, and repeat signals under the exact Node version you deploy. The project README's performance numbers are an old local benchmark, not a guarantee for your workload.

Patterns

Create a stoppable HTTP serverdecorate-http-server

const http = require('node:http')
const stoppable = require('stoppable')

const server = stoppable(http.createServer((req, res) => {
  res.end('ok')
}), 10_000)

server.listen(3000)

The second argument is the grace deadline in milliseconds. Without it, the default is Infinity and a stuck request can block forever.

Decorate an existing serverdecorate-existing-server

const server = http.createServer(app)
stoppable(server, 15_000)

server.listen(process.env.PORT || 3000)

stoppable mutates and returns the same server instance by adding stop and _pendingSockets properties.

Observe graceful versus forced shutdownstop-with-callback

server.stop((error, gracefully) => {
  if (error) {
    console.error('shutdown error', error)
    process.exitCode = 1
    return
  }
  console.log(gracefully ? 'drained requests' : 'forced remaining sockets')
})

The second callback argument is specific to stoppable. It becomes false when the grace timer runs its force-close path.

Wrap stop in a promiseawait-server-stop

function stopServer(server) {
  return new Promise((resolve, reject) => {
    server.stop((error, gracefully) => {
      if (error) reject(error)
      else resolve({gracefully})
    })
  })
}

const result = await stopServer(server)

The package itself is callback-only. This wrapper preserves the graceful flag instead of dropping it.

Stop once on SIGTERMhandle-termination-signal

let shuttingDown = false

process.on('SIGTERM', () => {
  if (shuttingDown) return
  shuttingDown = true

  server.stop((error, gracefully) => {
    if (error || !gracefully) process.exitCode = 1
  })
})

Guard repeat signals because stoppable does not document stop() as idempotent. Also close database pools and background consumers separately.

Use stoppable under Expressstop-express-server

const express = require('express')
const stoppable = require('stoppable')

const app = express()
app.get('/health', (req, res) => res.send('ok'))

const server = stoppable(app.listen(3000), 10_000)

Express returns the underlying Node HTTP server. This does not stop queues, scheduled work, or Express dependencies with their own handles.

Decorate an HTTPS serverstop-https-server

const https = require('node:https')
const stoppable = require('stoppable')

const server = stoppable(https.createServer(tlsOptions, app), 10_000)
server.listen(443)

The implementation listens to secureConnection for https.Server. This does not add HTTP/2 stream or WebSocket upgrade tracking.

Force sockets closed immediatelyforce-immediate-stop

const server = stoppable(http.createServer(handler), 0)

server.stop((error, gracefully) => {
  console.log({error, gracefully})
})

A grace value of 0 ends and destroys tracked sockets without waiting for active requests. Use it only when abrupt termination is intended.

Close a test server after each casetest-server-cleanup

afterEach(async () => {
  const {gracefully} = await stopServer(server)
  if (!gracefully) throw new Error('test server exceeded shutdown grace')
})

Use a finite grace in tests so one hung handler fails instead of leaving the test process open indefinitely.

Decorate and listen in one expressionchain-server-creation

const server = stoppable(
  http.createServer(handler),
  5_000
)

server.listen(3000, () => console.log('ready'))

The decorator returns the original server, so normal server methods remain available after wrapping.

Alternatives

PackageRegistryPick it when
http-terminatornpmYou want a purpose-built HTTP termination API with promise-based shutdown and more recent package design
http-graceful-shutdownnpmYou want signal hooks plus ordered cleanup callbacks around an HTTP server
@godaddy/terminusnpmYou need Kubernetes-oriented health checks, lifecycle signals, and server termination in one package
close-with-gracenpmYou need process-signal orchestration for several resources rather than socket tracking attached to one server