Features
What Corgi adds on top of fetch. All optional.
One client, or many
corgi for one-offs; corgi.create() for a configured client. Both are callable and have get/post/put/patch/delete/head.
await corgi.get('https://api.example.com/ping');
const api = corgi.create({ baseURL: 'https://api.example.com' });
await api.post('/users', { body: { name: 'Ada' } });Throws on non-2xx
No if (!res.ok). A 404/500 rejects with a typed HttpError carrying the status, parsed body, and a re-readable response.
try {
await corgi.get('/users/1');
} catch (err) {
if (isHttpError(err)) err.status; // 404
}Read more: Responses & errors →
Typed results
unknown by default, never any.
const user = await corgi.get<User>('/users/1'); // User
const text = await corgi.get('/x', { responseType: 'text' }); // stringRequest building
await api.get('/search', { query: { tag: ['a', 'b'], page: 2 } }); // ?tag=a&tag=b&page=2
await api.post('/users', { body: { name: 'Ada' } }); // JSON + content-type setRead more: Building requests →
Error guards
Name-based, so they survive iframes, workers, and duplicate bundles where instanceof fails.
isTimeoutError(err);
isAbortError(err);Opt-in plugins
Retry, timeout, cancel-previous, schema validation — each on its own import path, zero cost until imported.
const api = corgi.create({ plugins: [withRetry(3), withTimeout(5000)] });Derived clients
extend() — headers and plugins combine with the parent, not replace. Chonk clients can set retry, timeout, and abortPrevious here too.
const api = corgi.create({ headers: { authorization: 'Bearer t' } });
const billing = api.extend({ headers: { 'x-scope': 'billing' } }); // keeps authParses the response
By content-type — JSON, text, or Blob. Empty JSON → undefined (not a throw); unknown types → text (no JSON guessing).