no-floating-servers
Require Node.js server handles to be retained so they can be closed.
Rule catalog ID: R013
Targeted pattern scopeโ
This rule targets server factory calls from Node.js core networking modules:
http.createServerhttps.createServerhttp2.createServerhttp2.createSecureServernet.createServer- equivalent named imports, namespace imports, default imports, destructured
require(...)calls, and inlinerequire(...).createServer(...)calls
The rule reports server handles that are immediately discarded, including
discarded .listen(...) chains. It does not require type information because
the recognized factories are tied to import and require sources.
What this rule reportsโ
The rule reports:
- standalone server creation such as
createServer(handler); - voided server creation such as
void http.createServer(handler); - discarded listen chains such as
http.createServer(handler).listen(3000); - discarded method chains that start from an unretained server handle
It intentionally does not require same-function close() calls. Ownership can
be transferred to a framework adapter, test harness, returned value, or shutdown
manager.
Why this rule existsโ
Node.js server factories create long-lived handles that can keep the process
alive and hold open ports. Once a server is listening, retaining the Server
object is the normal way to call .close(), coordinate graceful shutdown, and
stop accepting new work.
Incorrectโ
import { createServer } from "node:http";
createServer(handler);
import http from "node:http";
http.createServer(handler).listen(3000);
const http2 = require("http2");
http2.createSecureServer(options, handler).listen(8443);
Correctโ
import { createServer } from "node:http";
const server = createServer(handler);
server.listen(3000);
server.close();
import * as http from "node:http";
const server = http.createServer(handler).listen(3000);
server.close();
return createServer(handler).listen(3000);
registerServer(createServer(handler).listen(3000));
Behavior and migration notesโ
Store the returned server handle in the owner that will shut it down. In applications, that is usually the process lifecycle or graceful-shutdown coordinator. In tests, it is usually the test fixture cleanup hook.
This rule allows immediate .close() calls and returned or passed server
handles. It reports discarded .listen(...) chains because listen() returns
the server object; discarding that returned handle makes later shutdown
coordination difficult.
This rule does not autofix. Choosing whether the owner is a module variable, dependency-injected lifecycle manager, framework adapter, or test fixture is a semantic decision.
ESLint flat config exampleโ
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
export default [
runtimeCleanup.configs.recommended,
];
When not to use itโ
Do not enable this rule for one-off scripts where the server is intentionally process-lifetime and never needs graceful shutdown. Prefer a narrow disable comment with a reason in those files rather than weakening the rule globally.