DEV Community

Cover image for I tried every axios alternative. None of them worked on Cloudflare Workers. So I built one.
Ramadan Ahmed
Ramadan Ahmed

Posted on

I tried every axios alternative. None of them worked on Cloudflare Workers. So I built one.

It started with a Cloudflare Worker.

I had a simple task, hit an external API from a Worker, return the data. I reached for axios like I always do. Deployed it. Got this:

Error: Cannot read properties of undefined (reading 'prototype')
Enter fullscreen mode Exit fullscreen mode

axios doesn't run on Cloudflare Workers. It depends on Node.js built-ins that the edge doesn't have. Fine. I'll switch.


The search for an alternative

Got — huge ecosystem, great docs. Dropped CommonJS in v12. My project uses require(). Hard pass.

Ky — clean API, zero deps. Browser-first. No Node.js support, no proxy, can't use it on the server side of my stack.

node-fetch — a polyfill. Node.js has had native fetch since v18. Why am I installing a polyfill for something that already exists?

redaxios — tiny, but it's just a thin axios-compatible wrapper around fetch. No retries, no interceptors, no auth helpers.

ofetch — closest to what I wanted, but no request deduplication, no in-memory cache, and the TypeScript types felt incomplete.

Every option had a wall. Edge runtime. CommonJS. Missing features. Another plugin needed.


So I built @firekid/hurl

Built on native fetch. Zero dependencies. Works on Node.js 18+, Cloudflare Workers, Vercel Edge, Deno, and Bun — no adapters, no polyfills, no config.

npm install @firekid/hurl
Enter fullscreen mode Exit fullscreen mode

That Cloudflare Worker that broke with axios? Here's what it looks like now:

import hurl from '@firekid/hurl'

export default {
  async fetch(request) {
    const res = await hurl.get('https://api.example.com/data', {
      retry: 3,
      timeout: 5000,
      auth: { type: 'bearer', token: ENV.API_TOKEN },
    })
    return new Response(JSON.stringify(res.data))
  }
}
Enter fullscreen mode Exit fullscreen mode

It just works. No error. No wall.


What it ships with out of the box

Everything I kept reaching for plugins to do — built in, zero extra installs:

Retries with exponential backoff

await hurl.get('/flaky-endpoint', {
  retry: {
    count: 4,
    backoff: 'exponential',
    on: [429, 500, 502, 503],
  }
})
Enter fullscreen mode Exit fullscreen mode

In-memory caching with TTL

// Won't hit the network again for 60 seconds
await hurl.get('/config', { cache: { ttl: 60000 } })
Enter fullscreen mode Exit fullscreen mode

Request deduplication

// 10 components call this simultaneously — only 1 network request fires
await hurl.get('/user', { deduplicate: true })
Enter fullscreen mode Exit fullscreen mode

Typed errors — no more optional chaining into error.response

import hurl, { HurlError } from '@firekid/hurl'

try {
  await hurl.get('/protected')
} catch (err) {
  if (err instanceof HurlError) {
    err.status    // 401
    err.data      // parsed error body
    err.retries   // how many times it retried before throwing
    err.type      // 'HTTP_ERROR' | 'TIMEOUT_ERROR' | 'ABORT_ERROR'
  }
}
Enter fullscreen mode Exit fullscreen mode

Interceptors

hurl.interceptors.request.use((url, options) => ({
  url,
  options: {
    ...options,
    headers: { ...options.headers, 'x-trace-id': crypto.randomUUID() },
  },
}))
Enter fullscreen mode Exit fullscreen mode

Isolated instances

const api = hurl.create({
  baseUrl: 'https://api.example.com',
  auth: { type: 'bearer', token: process.env.TOKEN },
  retry: 3,
})

const adminApi = api.extend({ headers: { 'x-role': 'admin' } })
Enter fullscreen mode Exit fullscreen mode

Migration from axios is 2 lines

// Before
import axios from 'axios'
const res = await axios.get('/users')

// After
import hurl from '@firekid/hurl'
const res = await hurl.get('/users')
Enter fullscreen mode Exit fullscreen mode

res.data, res.status, res.headers — same shape. It maps over cleanly.


The numbers

hurl axios got ky
Bundle size ~9KB ~35KB ~45KB ~5KB
Edge runtime
Node.js + CJS
Built-in retries
In-memory cache
Deduplication
Zero dependencies

It's live

I published it. 437 downloads in the first week, zero marketing. Just devs hitting the same walls I hit.

If you're on Cloudflare Workers, Vercel Edge, or just tired of the axios plugin ecosystem — give it a shot.

npm install @firekid/hurl
Enter fullscreen mode Exit fullscreen mode

Star it if it saves you a headache. Open an issue if it doesn't. 🔥


Built by Firekid

Top comments (0)