← All @molecule/* packages · App templates

@molecule/api-proxy-agent

Utility · proxy-agent · API (Node) · v1.1.0 · Apache-2.0

CONNECT-capable HTTP(S) proxy agent built from the standard proxy environment (HTTPS_PROXY / HTTP_PROXY / NO_PROXY), for vendor SDKs that build their own HTTP agent and ignore it.

npm install @molecule/api-proxy-agent

npm · Source on GitHub

How it works

@molecule/api-proxy-agent is a utility package for the API (Node) side (proxy-agent).

import { getProxyAgent, getProxyAgents } from '@molecule/api-proxy-agent'

// Stripe: `httpAgent` is used for every request the client makes.
const stripe = new Stripe(key, { httpAgent: getProxyAgent('https://api.stripe.com') })

// AWS SDK v3 (any client): `requestHandler` takes NodeHttpHandler options,
// so no extra `@smithy/*` dependency is needed.
const proxy = getProxyAgents(`https://email.${region}.amazonaws.com`)
const ses = new SESv2Client({ region, ...(proxy ? { requestHandler: proxy } : {}) })

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

A CONNECT-capable proxy agent built from the standard proxy environment, for the vendor SDKs that ignore it.

Most HTTP clients already honour HTTPS_PROXY/HTTP_PROXY/NO_PROXY: Node's global fetch does under NODE_USE_ENV_PROXY=1, and so do axios-based SDKs. A handful do not — they build their own http.Agent or undici pool and dial the vendor directly. On a workstation that direct connection succeeds, so the difference is invisible; in an environment whose ONLY egress path is a proxy (a molecule.dev sandbox, a deployed molecule.dev app, a locked-down VPC) the same call fails with a bare connection error naming nothing.

Each of those SDKs does accept an agent — it just will not build one for you. getProxyAgent(url) builds it, and returns undefined when no proxy applies so the SDK keeps its own default and an app running outside a proxied environment is completely unaffected.

Quick Start

import { getProxyAgent, getProxyAgents } from '@molecule/api-proxy-agent'

// Stripe: `httpAgent` is used for every request the client makes.
const stripe = new Stripe(key, { httpAgent: getProxyAgent('https://api.stripe.com') })

// AWS SDK v3 (any client): `requestHandler` takes NodeHttpHandler options,
// so no extra `@smithy/*` dependency is needed.
const proxy = getProxyAgents(`https://email.${region}.amazonaws.com`)
const ses = new SESv2Client({ region, ...(proxy ? { requestHandler: proxy } : {}) })

Type

utility

Installation

npm install @molecule/api-proxy-agent http-proxy-agent https-proxy-agent proxy-from-env
npm install -D @types/proxy-from-env

API

Interfaces

ProxyAgentOptions

Options for {@link getProxyAgent} / {@link getProxyUrl} / {@link shouldProxy}.

interface ProxyAgentOptions {
  /**
   * Keep the tunnelled socket alive between requests. Defaults to `true`,
   * matching what every SDK in this class does with its own default agent —
   * a proxy agent that closes the socket per request would silently make a
   * chatty SDK (S3 multipart, SQS long-poll) far slower than it was before.
   */
  keepAlive?: boolean
}

Types

ProxyAgent

An agent this package can hand to a vendor SDK. http: targets get an http.Agent, https: targets an https.Agent — both tunnel through the configured proxy.

type ProxyAgent = HttpAgent | HttpsAgent

Functions

getProxyAgent(targetUrl, options)

Returns an agent that tunnels targetUrl through the configured proxy via CONNECT, or undefined when this target is not proxied.

undefined is the whole contract: a call site passes the result straight into its SDK's own agent option, so with no proxy configured the SDK keeps its own default agent and behaves exactly as it did before. Nothing is monkey-patched and nothing changes for an app running outside a proxied environment.

function getProxyAgent(targetUrl: string, options?: ProxyAgentOptions): ProxyAgent | undefined
  • targetUrl — The absolute URL the SDK is about to call. It decides both which env var applies (https_proxy vs http_proxy) and whether NO_PROXY exempts the host, so pass the real vendor endpoint — not a placeholder.
  • options — See {@link ProxyAgentOptions}.

Returns: An http.Agent/https.Agent that tunnels through the proxy, or undefined when the target is not proxied.

getProxyAgents(targetUrl, options)

Returns { httpAgent } or { httpsAgent } — whichever matches targetUrl's protocol — or undefined when the target is not proxied.

This is the shape AWS SDK v3's requestHandler accepts directly (it takes NodeHttpHandler OPTIONS, so no @smithy/node-http-handler dependency is needed), and it is also what got and node-fetch take. Spread it, so that an unproxied environment adds no key at all:

const proxy = getProxyAgents(endpoint)
new S3Client({ region, ...(proxy ? { requestHandler: proxy } : {}) })
function getProxyAgents(
  targetUrl: string,
  options?: ProxyAgentOptions,
): { httpAgent?: ProxyAgent; httpsAgent?: ProxyAgent } | undefined
  • targetUrl — The absolute URL the SDK is about to call.
  • options — See {@link ProxyAgentOptions}.

Returns: The agent pair, or undefined when the target is not proxied.

getProxyUrl(targetUrl)

Returns the proxy URL that should serve targetUrl according to the standard proxy environment (HTTPS_PROXY / HTTP_PROXY / ALL_PROXY, either case), or undefined when the target is not proxied — either because no proxy is configured, or because NO_PROXY exempts it.

function getProxyUrl(targetUrl: string): string | undefined
  • targetUrl — The absolute URL the SDK is about to call.

Returns: The proxy URL, or undefined to connect directly.

resetProxyAgents()

Drops every memoized agent, destroying its sockets.

Only useful when the proxy environment changes inside a live process — a test that mutates process.env, or a secrets bond that resolves HTTPS_PROXY after the first call. Ordinary application code never needs it.

function resetProxyAgents(): void

shouldProxy(targetUrl)

Whether targetUrl should be sent through a proxy.

function shouldProxy(targetUrl: string): boolean
  • targetUrl — The absolute URL the SDK is about to call.

Returns: true when a proxy is configured for this target.

Injection Notes

Runtime Dependencies

  • http-proxy-agent

  • https-proxy-agent

  • proxy-from-env

  • Pass the REAL endpoint, not a placeholder. The target URL decides which variable applies (https_proxy vs http_proxy) and whether NO_PROXY exempts the host. getProxyAgent('https://example.com') standing in for an S3 call would consult the wrong NO_PROXY entry.

  • Spread the result, never pass it unconditionally. Several SDKs treat a present-but-undefined agent option as "use no agent" rather than "use the default". ...(agent ? { httpAgent: agent } : {}) is the shape that is a true no-op when nothing is proxied.

  • Agents are memoized per proxy URL, because an agent owns a connection pool — building a fresh one per call opens a new tunnel every request. Call it inside your lazy client getter and keep the client, not the agent.

  • This cannot rescue an SDK that vendors its own proxy handling. If a library bundles a copy of its HTTP client and offers no agent option, there is no hook to pass this into; the fix has to be upstream or in the proxy.

  • HTTP(S) only. A raw-TCP client — Postgres, MySQL, Redis, Mongo, AMQP, SMTP — cannot be tunnelled by an HTTP proxy at all. Those need a network path to the host, not an agent.

  • Built on https-proxy-agent/http-proxy-agent (the CONNECT tunnel) and proxy-from-env (the env + NO_PROXY semantics, the same resolver axios uses) rather than a hand-rolled parser: NO_PROXY has enough real edge cases — leading dots, *, per-entry ports, IPv6 brackets — that a bespoke one would be wrong in exactly the situations it matters.