DEV Community

Cover image for I got tired of writing the same axios wrapper on every project, so I built this
Ramadan Ahmed
Ramadan Ahmed

Posted on

I got tired of writing the same axios wrapper on every project, so I built this

**Every project I worked on had the same file. Some version of lib/http.ts or utils/api.ts that did the exact same thing, wrap axios, add retry logic, handle auth, set a base URL, catch errors properly. Copy paste. Rename. Repeat.
After doing this for the fifth time I thought, why am I still doing this?
The problem isn't axios itself. Axios is fine. But it's missing things that every production app needs, and you end up building them yourself every single time:
You want retries? Write it yourself.
You want timeout that actually works? Write it yourself.
You want upload progress? Axios doesn't even support it.
You want request deduplication? Good luck.
And then there's request, deprecated since 2020, still getting millions of downloads a week because nothing properly replaced it.
So I built @firekid/hurl.
Before
import axios from 'axios'

const client = axios.create({ baseURL: 'https://api.example.com' })

client.interceptors.request.use((config) => {
config.headers.Authorization = Bearer ${token}
return config
})

try {
const res = await client.get('/users')
console.log(res.data)
} catch (err) {
if (err.response) {
console.log(err.response.status)
}
}
After
import hurl from '@firekid/hurl'

hurl.defaults.set({
baseUrl: 'https://api.example.com',
auth: { type: 'bearer', token },
retry: 3,
})

const res = await hurl.get('/users')
console.log(res.data)

Same result. Half the code. Retry built in.
What hurl ships with out of the box
Everything you'd normally write yourself, retries with exponential backoff, timeout and abort controller support, request and response interceptors, Bearer, Basic and API key auth, upload and download progress tracking, in-memory caching with TTL, request deduplication, proxy support, and full TypeScript types with zero config.
It also throws on 4xx and 5xx automatically. No more checking err.response manually. If something goes wrong you get a proper HurlError with the status, the response body, and how many retries were attempted.
It works everywhere fetch works
Node.js 18+, Cloudflare Workers, Vercel Edge Functions, Deno, Bun. Same code, no adapter needed. Axios doesn't run on edge runtimes. hurl does.

Getting started
npm install @firekid/hurl
import hurl from '@firekid/hurl'

const res = await hurl.get('https://api.example.com/users')
console.log(res.data)
For a real app:
const api = hurl.create({
baseUrl: 'https://api.example.com',
auth: { type: 'bearer', token: process.env.API_TOKEN },
retry: 3,
timeout: 8000,
})

const users = await api.get('/users')
const post = await api.post('/posts', { title: 'hello' })
That's it. No configuration files, no adapters, no extra packages.
If you've ever found yourself writing the same HTTP wrapper for the fifth time, give it a try.
GitHub: github.com/Firekid-is-him/hurl
npm: npmjs.com/package/@firekid/hurl**

Top comments (0)