upfetch
Use up-fetch when you need a reusable fetch client with request-scoped defaults, automatic request/response shaping, runtime validation, retries, and lifecycle hooks.
Mental model
up(fetchFn, getDefaultOptions?)creates the reusable client.getDefaultOptions(input, options, ctx)runs on every request.upfetch(input, options?, ctx?)performs one request.- Keep
SKILL.mdhigh-level; load the relevant file underreferences/for details.
Minimum pattern
import { up } from 'up-fetch'
import { z } from 'zod'
export const upfetch = up(fetch, () => ({
baseUrl: 'https://api.example.com',
headers: {
Authorization: readToken() ? `Bearer ${readToken()}` : undefined,
},
timeout: 5000,
}))
const user = await upfetch('/users/1', {
schema: z.object({
id: z.number(),
name: z.string(),
}),
})
Workflow
- Start with client setup and dynamic defaults.
- If the request needs auth, params, body shaping, or merge semantics, read auth and request shaping.
- If the response contract matters, read validation, parsing, and errors.
- If retries, timeouts, or hook timing matter, read retries, timeouts, and lifecycle.
- If streaming or runtime quirks matter, read streaming and runtime caveats.
High-value rules
- Pass a function as the second argument to
up(), not a plain object. - Read auth and other mutable defaults inside that function so values stay fresh.
- Use
paramsandbodyinstead of hand-serializing query strings or JSON. - Use
schemawhen you need runtime trust; TypeScript generics alone do not validate payloads. - If you want error-as-value behavior, set
reject: () => falsebefore relying onparseResponse. - Prefer
globalThis.fetchover importedundici.fetch.