The api-client cheat sheet: every config option and method in one place
Everything else in this series explains why a feature works the way it does. This post skips the why — it’s the reference I actually want open in a second tab while I’m writing config, not something meant to be read top-to-bottom. Each section links conceptually back to the post that covers the reasoning, if you need it.
createClient(config)
| Key | Type | Covered in |
|---|---|---|
baseURL | string | Part 2 |
openapi.mode | 'runtime' | 'codegen' | Parts 2, 11 |
openapi.validateResponses | boolean | Part 12 |
openapi.onValidationFailure | 'warn' | 'throw' | Part 12 |
auth.strategy | 'bearer' | 'cookie' | 'apiKey' | 'oauth2' | Part 5 |
http.timeout | number (ms) | Parts 4, 7 |
http.retry.attempts | number | Part 7 |
http.retry.backoff | 'exponential' | 'linear' | Part 7 |
http.retry.retryOn | number[] (status codes) | Part 7 |
cache.strategy | 'cache-first' | 'network-first' | 'stale-while-revalidate' | Part 6 |
cache.ttl | number (ms) | Part 6 |
concurrency.max | number | Part 7 |
tenancy.getTenantId | () => string | Promise<string> | Part 8 |
environments | Record<string, { baseURL: string }> | Part 8 |
env | string | Part 8 |
safeMode | boolean | Part 9 |
hooks.beforeRequest | (config) => config | Part 10 |
hooks.afterResponse | (response) => response | Part 10 |
hooks.onError | (error) => void | any | Part 10 |
modules | Record<string, ModuleDefinition> | Part 2 |
defineModule({ config, methods })
Module-level config accepts the same keys as createClient’s baseURL,
timeout, headers, auth, cache, retry, tenancy, validation —
overriding the global value for every method in that module. See Part 3 and
Part 4 for the merge order.
defineModule({
config: { timeout: 20_000 },
methods: {
methodName: async (ctx, ...args) => ctx.request({ /* spec */ }),
},
})ctx.request(spec, perCall?)
spec:
| Key | Type | Notes |
|---|---|---|
method | 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | |
path | string | {placeholders} for path params |
pathParams | Record<string, string> | Required if path has placeholders |
query | Record<string, unknown> | |
body | unknown |
perCall (PerCallConfig):
| Key | Type | Covered in |
|---|---|---|
signal | AbortSignal | Part 7 |
headers | Record<string, string> | Part 4 |
tenantId | string | Part 8 |
cache.enabled | boolean | Part 6 |
cache.ttl | number | Part 6 |
cache.bust | boolean | Part 6 |
retry.attempts | number | Part 7 |
timeout | number | Part 4 |
skipAuth | boolean | Part 5 |
skipDedup | boolean | Part 7 |
safeMode | boolean | Part 9 |
responseType | 'json' | 'blob' | 'text' | 'arraybuffer' |
Returns ApiResponse<T>:
{ data: T, status: number, statusText?: string, headers: Record<string, string>, fromCache?: boolean }See Part 3.
Error classes
| Class | Thrown when | Covered in |
|---|---|---|
ApiError | Base class for all API errors | Part 9 |
NetworkError | No response at all (offline, DNS) | Part 9 |
TimeoutError | Request exceeded timeout, after retries exhausted | Parts 7, 9 |
ValidationError | 4xx with structured error body | Part 9 |
AuthError | 401/403, or refresh failure | Parts 5, 9 |
ConfigurationError | Your code called the client wrong | Part 9 |
Cache methods
ctx.cache.invalidate(key: string): Promise<void>
ctx.cache.invalidatePattern(pattern: string): Promise<void>See Part 6.
Events (api.on(event, handler))
| Event | Payload | Covered in |
|---|---|---|
request:start | { key } | Part 10 |
request:end | { key, status } | Part 10 |
cache:hit | { key } | Parts 6, 10 |
cache:miss | { key } | Part 6 |
cache:invalidate | { pattern } | Part 6 |
retry | { attempt, key } | Part 7 |
auth:refresh | — | Part 5 |
Codegen CLI commands
| Command | Purpose | Covered in |
|---|---|---|
generate --spec --out | Generate types + descriptors once | Part 11 |
watch --spec --out | Regenerate on save, local dev | Part 11 |
validate --spec | Check spec well-formedness | Part 11 |
diff --old --new | Categorize spec changes as breaking/non-breaking | Part 11 |
drift-check --spec --baseURL | Compare live backend against committed spec | Part 12 |
RPC bridge
// server
createRpcHandler(realClient, { expose: { moduleName: ['methodName'] } })
// browser
createRpcClient({ endpoint: '/api/rpc' })Deny-by-default: only { module, method } pairs listed in expose are
reachable. See Parts 14–15 for the full architecture and security model.
TanStack Query integration
useApiQuery(api.module.method, [args])
useApiMutation(api.module.method, { invalidates: [api.otherModule.otherMethod] })Available for React, Vue, and Solid, with identical key-generation semantics. See Part 13.
Testing
createMockClient({
moduleName: { methodName: async (...args) => mockReturnValue },
})See Part 17.
What this post is for
Not a takeaway section this time — this whole post is the takeaway from the series so far, compressed. If a row here doesn’t make sense on its own, that’s exactly what the linked post is for.
Next: troubleshooting and FAQ — the errors I’ve actually hit using this package, told as the debugging stories they actually were.
- developerehsan