Skip to the solution
HardSystem Design6 min read

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

784 views

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."

  • http
  • networking
  • agent

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 9 sections

Read the code

JavaScriptA 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

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 129 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextHard

What changes for a Node.js app running on AWS Lambda vs a long-running server?

Continue