axios had a great run.
For years it was the HTTP client. You installed it without thinking. You recommended it without question. It just worked.
But it's 2026. Node.js has had native fetch since v18. Cloudflare Workers, Vercel Edge Functions, Deno, and Bun are mainstream. And axios — a 35KB library built around XMLHttpRequest — isn't built for any of that.
I built @firekid/hurl to fix that.
What's wrong with axios?
Let me be specific, because "axios is old" isn't an argument.
1. It doesn't run on edge runtimes.
Cloudflare Workers, Vercel Edge, and Deno don't support Node.js built-ins. axios depends on them. You hit a wall the moment you try to use it outside of a traditional Node.js server.
2. It's 35KB.
For a package that wraps HTTP requests. @firekid/hurl is under 9KB gzipped.
3. No built-in retries.
You either write your own retry logic or reach for axios-retry — another dependency, more config, more maintenance.
4. No request deduplication.
If your app fires the same GET request three times simultaneously, axios makes three network calls. Every time.
5. No in-memory caching.
Out of the box, zero cache support.
It's not that axios is broken. It's that the ecosystem has moved on and axios hasn't.
Enter hurl
npm install @firekid/hurl
Zero dependencies. Full TypeScript support. Under 3KB gzipped. Works on Node.js 18+, Cloudflare Workers, Vercel Edge, Deno, and Bun.
Here's what a real-world usage looks like:
import hurl from '@firekid/hurl'
// Automatic retry with exponential backoff
const res = await hurl.get('https://api.example.com/users', {
retry: 3,
timeout: 5000,
auth: { type: 'bearer', token: process.env.API_TOKEN },
cache: { ttl: 60000 },
})
console.log(res.data) // parsed body
console.log(res.timing) // { start, end, duration }
console.log(res.fromCache) // boolean
That's retries, timeout, auth, and caching — no plugins, no wrappers, no extra packages.
The comparison you actually want
| Feature | hurl | axios | ky | got |
|---|---|---|---|---|
| Bundle size | ~9KB | ~35KB | ~5KB | ~45KB |
| Edge runtime support | ✅ | ❌ | ✅ | ❌ |
| Built-in retries | ✅ | ❌ | ✅ | ✅ |
| In-memory cache | ✅ | ❌ | ❌ | ❌ |
| Request deduplication | ✅ | ❌ | ❌ | ❌ |
| Auth helpers | ✅ | ⚠️ | ❌ | ❌ |
| Interceptors | ✅ | ✅ | ✅ | ❌ |
| CommonJS + ESM | ✅ | ✅ | ❌ | ❌ |
| Zero dependencies | ✅ | ❌ | ✅ | ❌ |
Things hurl does that axios simply can't
Works on Cloudflare Workers and Vercel Edge
// This just works — no adapter, no polyfill
export default {
async fetch(request) {
const res = await hurl.get('https://api.example.com/data')
return new Response(JSON.stringify(res.data))
}
}
Retry with full control
await hurl.get('/unstable-endpoint', {
retry: {
count: 4,
delay: 500,
backoff: 'exponential',
on: [500, 502, 503, 429],
}
})
Request deduplication
// Only ONE network request is made — both get the same response
const [a, b] = await Promise.all([
hurl.get('/users', { deduplicate: true }),
hurl.get('/users', { deduplicate: true }),
])
In-memory caching with TTL
// Cache for 60 seconds
await hurl.get('/config', { cache: { ttl: 60000 } })
Isolated instances (like axios.create, but better)
const api = hurl.create({
baseUrl: 'https://api.example.com',
auth: { type: 'bearer', token: process.env.TOKEN },
retry: 3,
timeout: 8000,
})
const adminApi = api.extend({
headers: { 'x-role': 'admin' }
})
Typed errors — no more error.response?.data
import hurl, { HurlError } from '@firekid/hurl'
try {
await hurl.get('/protected')
} catch (err) {
if (err instanceof HurlError) {
console.log(err.status) // 401
console.log(err.data) // parsed error body
console.log(err.retries) // how many times it retried
console.log(err.type) // 'HTTP_ERROR' | 'TIMEOUT_ERROR' | 'ABORT_ERROR' ...
}
}
What about ky and got?
ky is great — but it's browser-first. No proxy support, no Node.js streaming, dropped CommonJS.
got is powerful — but dropped CommonJS in v12, making it incompatible with most existing Node.js projects, and it's 45KB.
@firekid/hurl is the only one that runs everywhere, supports both CJS and ESM, and ships everything you need without external plugins.
Migration from axios takes 5 minutes
// Before
import axios from 'axios'
const res = await axios.get('/users')
console.log(res.data)
// After
import hurl from '@firekid/hurl'
const res = await hurl.get('/users')
console.log(res.data)
The API is intentionally familiar. res.data, res.status, res.headers — it all maps over.
Try it
npm install @firekid/hurl
If this saved you from another axios-retry install, drop a ⭐ on GitHub. And if you find a bug or want a feature, issues are open.
Built by Firekid — feedback welcome.
Top comments (0)