@molecule/api-proxy-agent
v1.1.0
Published
CONNECT-capable HTTP(S) proxy agent built from the standard proxy environment, for vendor SDKs that ignore it.
Maintainers
Readme
@molecule/api-proxy-agent
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.tsJSDoc, 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-envAPI
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 | HttpsAgentFunctions
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 | undefinedtargetUrl— The absolute URL the SDK is about to call. It decides both which env var applies (https_proxyvshttp_proxy) and whetherNO_PROXYexempts 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 } | undefinedtargetUrl— 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 | undefinedtargetUrl— 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(): voidshouldProxy(targetUrl)
Whether targetUrl should be sent through a proxy.
function shouldProxy(targetUrl: string): booleantargetUrl— 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-agenthttps-proxy-agentproxy-from-envPass the REAL endpoint, not a placeholder. The target URL decides which variable applies (
https_proxyvshttp_proxy) and whetherNO_PROXYexempts the host.getProxyAgent('https://example.com')standing in for an S3 call would consult the wrongNO_PROXYentry.Spread the result, never pass it unconditionally. Several SDKs treat a present-but-
undefinedagent 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) andproxy-from-env(the env +NO_PROXYsemantics, the same resolver axios uses) rather than a hand-rolled parser:NO_PROXYhas enough real edge cases — leading dots,*, per-entry ports, IPv6 brackets — that a bespoke one would be wrong in exactly the situations it matters.
