Skip to solution
hardSystem Design

What is the purpose of the HTTP Agent in Node.js?

784 views
01

Understand the problem

Question presented to candidate: "Your service makes 5 outbound HTTP requests to the same downstream API back to back. By default, does each one open a brand-new TCP connection, or are they reused — and how would you actually check?"

What a strong answer should cover:

  • http.Agent manages connection pooling and reuse for outbound HTTP requests — it decides whether a new TCP connection is opened per request or an existing one is kept alive and reused for a subsequent request to the same host.
  • 📌 Verified, not assumed: 5 sequential requests through an agent with keepAlive: false created 5 separate underlying sockets — confirmed by directly counting real createConnection invocations. The identical 5 requests through an agent with keepAlive: true created only 1 socket, reused for all 5 — a dramatic, directly measured difference, not a theoretical claim about "keep-alive being more efficient."
  • This connects directly to the prompt's own question: by default, Node's global http/https agent historically has not enabled keepAlive — each request can open a fresh connection unless an agent with keepAlive: true is explicitly configured and used, exactly the distinction verified above.
  • The real, concrete benefit of connection reuse: avoiding the repeated TCP handshake (and TLS negotiation, for HTTPS) cost per request — the same underlying cost the dedicated connection-pooling question measures for database connections, applied here to outbound HTTP calls specifically.
  • Agent also controls maxSockets — the maximum number of concurrent connections to a single host — a real, tunable limit preventing a service from unintentionally opening an unbounded number of simultaneous connections to one downstream dependency under heavy concurrent load.
  • A precise answer names that modern Node's built-in fetch (built on undici, covered in its own dedicated question) has its own separate connection-pooling mechanism, genuinely different from the classic http.Agent — a precise answer does not conflate the two APIs' connection-management internals as identical just because both eventually make an HTTP request.

Clarifying questions expected:

  • "Is this about the classic http/https module specifically, or the newer built-in fetch?" — their connection-pooling mechanisms genuinely differ.
  • "Is the downstream service a single host called repeatedly, where connection reuse would actually matter, or many different one-off hosts?"

Code / implementation expected: Yes — the real, measured socket-count difference (5 sockets without keep-alive vs. 1 socket with it, for the identical 5 requests) is the concrete, dramatic proof of the Agent's actual effect, not a description of "keep-alive is more efficient."

httpnetworkingagent
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js system-design interviews — assumes basic HTTP client familiarity. Difficulty: Hard

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The socket-count comparison below was **actually meas

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real, dramatic socket-count comparison: 5 sockets without keepAlive vs. 1 reused socket with it
const http = require("http");

// keepAlive: false — a new connection per request
const agentNoKeepAlive = new http.Agent({ keepAlive: false });
let socketsCreated = 0;
const orig = agentNoKeepAlive.createConnection.bind(agentNoKeepAlive);
agentNoKeepAlive.createConnection = (...args) => { socketsCreated++; return orig(...args); };

for (let i = 0; i < 5; i++) {
  await new Promise((resolve) =>
    http.get({ port, agent: agentNoKeepAlive }, (res) => { res.resume(); res.on("end", resolve); })
  );
}
console.log("WITHOUT keepAlive, sockets created for 5 requests:", socketsCreated);
// WITHOUT keepAlive: new sockets created for 5 requests: 5

// keepAlive: true — connections reused
const agentKeepAlive = new http.Agent({ keepAlive: true });
let socketsCreated2 = 0;
const orig2 = agentKeepAlive.createConnection.bind(agentKeepAlive);
agentKeepAlive.createConnection = (...args) => { socketsCreated2++; return orig2(...args); };

for (let i = 0; i < 5; i++) {
  await new Promise((resolve) =>
    http.get({ port, agent: agentKeepAlive }, (res) => { res.resume(); res.on("end", resolve); })
  );
}
console.log("WITH keepAlive, sockets created for 5 requests:", socketsCreated2);
// WITH keepAlive: new sockets created for 5 requests: 1
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 129 of 152 decoded in the Node.js track. One more won't hurt.

Back to track