Build a typed API client wrapper with retries, streaming, and structured errors
Produces a production-grade API client wrapper around an OpenAI/Anthropic-style API with typed requests and responses, streaming, exponential-backoff retries, and structured errors, instead of a bare fetch that dies on the first 429.
You are a senior backend engineer who writes resilient API integrations. Build a typed client wrapper around an external API. Context: - API: [OpenAI / Anthropic / a generic REST API — name it] - Language: [TypeScript / Python] - Auth: [Bearer token / API key in header / OAuth] - Needs streaming: [yes — SSE token stream / no] - Runtime: [Node 20+ / Deno / Bun / Python 3.11+] Build a client with: 1. A typed request and response surface. Define input types and response types; public methods return typed results, not any or unknown leaking out. 2. Streaming support (if requested): an async iterator that yields decoded tokens or events as they arrive, with backpressure-safe consumption and a clean way to abort via AbortSignal. 3. Retry with exponential backoff plus jitter on transient errors (429, 5xx, network errors). Respect Retry-After when present. No retry on 4xx except 429. Max retries and total timeout configurable with sane defaults. 4. Structured errors: a typed error hierarchy (AuthError, RateLimitError, ServerError, NetworkError, ValidationError) so callers can handle each case; never throw a bare string. 5. Request and response interception hooks so logging or metrics can be plugged in without changing call sites. 6. Secret handling: keys read from env, never logged, never embedded in error messages. 7. Sensible defaults for timeout, max retries, and per-call abort, all overridable per request. Requirements: - No silent swallowing of errors. Every failure surfaces a typed error. - No unbounded concurrency assumptions; the wrapper is stateless and safe to share as a singleton. - Keep dependencies minimal; prefer the platform fetch or stdlib. Output, in this exact order: 1. A short design rationale (retry policy, error model, streaming approach). 2. The full client module with typed interfaces and methods. 3. A usage example for a non-streaming call and (if requested) a streaming call with abort. 4. An error-handling example showing each typed error being caught and handled. 5. A test checklist: 429-then-success, 5xx-then-give-up, abort mid-stream, malformed response. Success signal: the output is good only if responses are typed end to end, retries respect Retry-After and skip non-transient errors, streaming is abortable, and failures always surface a typed error — never a bare throw or a silent catch.
Use case
Use when you are calling an LLM or REST API from production code and need robust retries, typed responses, and streaming rather than a throwaway curl-style call.
When to use this
Before shipping any code that calls an external API in production. Not for one-off scripts where a bare fetch is fine.
Follow-up prompts
- Add request and response logging with redaction of secrets and PII.
- Add a circuit breaker that stops hammering after repeated failures.
- Generate a JSON-Schema type for the response and wire it into the client.
- Source
- promptfork seed
- License
- CC-BY-4.0
- Published
- 6/22/2026