import { Agent as HttpAgent } from 'http';
import { Agent as HttpsAgent } from 'https';
const shared = {
keepAlive: true,
keepAliveMsecs: 1_000,
maxSockets: 64,
maxFreeSockets: 16,
timeout: 30_000,
scheduling: 'lifo' as const,
};
export const httpAgent = new HttpAgent(shared);
export const httpsAgent = new HttpsAgent(shared);
export function agentStats() {
return {
http: {
free: Object.values(httpAgent.freeSockets).flat().length,
active: Object.values(httpAgent.sockets).flat().length,
},
https: {
free: Object.values(httpsAgent.freeSockets).flat().length,
active: Object.values(httpsAgent.sockets).flat().length,
},
};
}
import axios, { AxiosInstance } from 'axios';
import { httpAgent, httpsAgent } from './agents';
export function createHttpClient(baseURL: string): AxiosInstance {
const client = axios.create({
baseURL,
timeout: 10_000,
httpAgent,
httpsAgent,
headers: { Connection: 'keep-alive' },
});
client.interceptors.response.use((res) => {
const reused = (res.request as any)?.reusedSocket === true;
res.headers['x-socket-reused'] = String(reused);
return res;
});
return client;
}
export async function shutdownAgents(): Promise<void> {
httpAgent.destroy();
httpsAgent.destroy();
}
import { Server } from 'http';
import { shutdownAgents } from './httpClient';
import { agentStats } from './agents';
export function installShutdown(server: Server): void {
let closing = false;
const handler = async (signal: string) => {
if (closing) return;
closing = true;
console.log(`${signal} received, draining. sockets=`, agentStats());
server.close((err) => {
if (err) {
console.error('server close failed', err);
process.exit(1);
}
});
await shutdownAgents();
setTimeout(() => process.exit(0), 200).unref();
};
process.on('SIGTERM', () => handler('SIGTERM'));
process.on('SIGINT', () => handler('SIGINT'));
}
Every outbound HTTP call from a Node service can pay a hidden tax: opening a fresh TCP connection (and a TLS handshake for HTTPS) before a single byte of the request is sent. Under load this adds latency and burns file descriptors and ephemeral ports. The fix is connection reuse via keep-alive http.Agent/https.Agent instances that pool sockets across requests, and this snippet shows how to wire that pooling into a shared client.
In agents.ts, two singleton agents are created with keepAlive: true and a keepAliveMsecs interval so the OS sends TCP keep-alive probes on idle sockets. maxSockets caps concurrent connections per host so a burst cannot exhaust upstream capacity, while maxFreeSockets bounds how many idle sockets are retained for reuse. scheduling: 'lifo' is chosen deliberately: last-in-first-out keeps a small hot set of sockets warm and lets the rest idle out via timeout, which plays better with load balancers that reap idle connections than round-robin would. Exporting these as module-level singletons is essential — creating an agent per request defeats the entire purpose, since each new agent owns its own pool.
httpClient.ts builds an axios instance that hands the correct agent to each protocol through httpAgent and httpsAgent. A response interceptor reads res.request.reusedSocket to expose whether a call reused a pooled connection, which is invaluable when verifying the pool is actually working in production. The shutdownAgents helper calls destroy() on both agents so in-flight sockets are closed cleanly during graceful shutdown, avoiding leaked connections when the process exits.
gracefulShutdown.ts ties it together, draining the HTTP server and then tearing down the agent pools on SIGTERM. The main trade-off of keep-alive is stale sockets: an upstream or proxy may silently close a connection that the client still believes is open, surfacing as ECONNRESET. Bounding idle lifetime with timeout and retrying idempotent requests on that specific error mitigates it. This pattern is worth reaching for whenever a service makes repeated calls to the same handful of hosts.
Related snips
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
Rails.application.configure do
config.after_initialize do
Bullet.enable = true
Bullet.alert = false
Bullet.bullet_logger = true
Bullet.console = true
N+1 query detection with Bullet gem
json.array! @posts do |post|
json.cache! ['v1', post], expires_in: 1.hour do
json.id post.id
json.title post.title
json.excerpt post.excerpt
json.published_at post.published_at
Fragment caching for expensive JSON serialization
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
timeout: 15000,
Axios API client with interceptors
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface FilterState {
search: string
category: string | null
Zustand for lightweight state management
Share this code
Here's the card — post it anywhere.