nanofetch
A ~5KB, zero-dependency, type-safe HTTP client with an Axios-like API, built entirely on native fetch.
$ npm install @hnrie/nanofetch
import { api } from '@hnrie/nanofetch';
const res = await api.get('/users');A colleague had dropped Axios after one of its supply-chain incidents and gone back to plain fetch, losing interceptors, retries and typed errors. Could you keep Axios’s ergonomics with zero dependencies?
- Step 01
An Axios-compatible API (instances, interceptors, one call for GET/POST), so switching costs almost nothing.
- Step 02
Works in browsers, Node.js and React Native, shipped as ESM and CJS.
- Step 03
Request and response interceptor chains; a rejected handler that resolves short-circuits the chain.
- Step 04
Retries with a fresh AbortController per attempt, cleaned up in each attempt’s finally block.
- Step 05
Every error is a structured ApiError carrying method, URL and config.
Timeout and cancel are the same mechanism, not the same thing
Both abort through AbortController and look identical in a catch block. A Symbol as the timeout’s abort reason lets the handler tell them apart.
A consumed abort signal can’t be reused
Retrying on the previous attempt’s controller fails instantly. Each attempt gets its own controller.
Parse errors should be typed, not raw
The body is read as text and parsed in a try/catch, so a malformed JSON body becomes a client error with isParseError instead of a raw SyntaxError.
Token refresh the caller never sees
A 401 handler refreshes the token and calls replay(), rebuilt from the metadata on the error, so the original request just succeeds.
A fetch wrapper looks like fifty lines until you list what Axios actually handles for you.