Client cloning
withOptions(partial) returns a new client built from the original one’s options. The original client is untouched, and the new one reuses its hooks and retry settings. defaultHeaders is merged with the original headers; any other option you pass replaces the original value.
defaultHeaders is sent on every request, but Nuntly does not read or route on it. Use it for your own tracing and audit chain.
One API key per customer
When each customer of your SaaS has their own Nuntly API key, a newNuntly per request would need the hooks and the retry settings passed again. Clone the root client instead:
One trace ID across services
When a request in your app calls several services, forward your trace ID on every Nuntly call so your APM links the email send to the request that triggered it:Who sent this email
To record which user of your app sent an email, stamp a header on every request. Your gateway, proxy or log pipeline records it; Nuntly ignores it.Lifecycle hooks
Hooks run around each HTTP request, for logging or telemetry. A hook reads the request and cannot change it. Every hook receives a singlectx argument:
For one call, the hooks run in this order:
onRequest, then onRetry and onRequest again for each retry, then onResponse, then onSuccess or onError. The client awaits every hook that returns a Promise.
Retries
By default the SDK retries a request up to 2 times on a 408, a 429, a 500, a 502, a 503, a 504, a timeout or a network error. The first retry waits 375 to 500 ms and each later one twice as long, up to 8 s. The initial interval is 500 ms, the exponent is 2, and a jitter shortens each delay to 75% to 100% of it.- A 409 is retried only when the API sends
x-should-retry: true. The API does so when the first attempt of a send with an idempotency key is still in progress, and the retry gets the stored response of that attempt. Any other conflict, such as a contact that already exists, does not resolve on its own. - A
POSTwithout anIdempotency-Keyis retried only on a 429, so a retry cannot create a duplicate. The eight sending methods listed in the README’s per-request options generate a key when you pass none. - The SDK never retries a request you abort through
signal, including withAbortSignal.timeout(...), or an error thrown by a hook. - A server-sent
x-should-retry: trueorfalseoverrides these rules. ARetry-Afterheader (seconds, decimals allowed, or an HTTP date) on any response replaces the backoff delay. Above 60 s the SDK does not wait: it throws the error for that response at once, withRateLimitError.retryAfterset on a 429. - With
retry: 'none', a per-requestmaxRetriesdoes not turn retries back on.
Safe mode for one call
createSafeNuntly makes every call return { data, error }. To get that shape for some calls only, wrap an existing client with safe():
Response headers without the body
.asResponse() resolves to the raw Response as soon as the headers arrive, without reading the body. Use it for streaming or to read headers only. It does not throw on a 4xx or 5xx, so check response.status:
Abort a request
Debug logging
debug: true logs each request and response:
Custom logger
logger accepts any logger compatible with console, such as pino or winston:
Custom fetch
fetch replaces globalThis.fetch with any compatible function, for instrumentation, custom retry or another HTTP client. The SDK calls it with a Request as its first argument:
HTTP and HTTPS proxies
On Node.js, pass anundici ProxyAgent as the dispatcher of the global fetch to send requests through a corporate or debugging proxy:
