Fetch vs Axios: Which Should You Use for HTTP Requests?
fetch is built into browsers and Node.js; Axios is a popular library with extra conveniences. How they differ on JSON handling, errors, timeouts, interceptors and upload progress, the same request in both, and why fetch is usually enough now.
To call an API from JavaScript, you'll use either fetch — built into every modern browser and into Node.js 18+ — or Axios, a library you install. Both work. The differences are in the conveniences.
The same request in both
fetch:
const res = await fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId: 42 }),
})
if (!res.ok) throw new Error(`HTTP ${res.status}`)
const order = await res.json()
Axios:
import axios from 'axios'
const { data: order } = await axios.post('/api/orders', { productId: 42 })
Axios is shorter because it does three things for you: converts the body to JSON, parses the JSON response, and throws on error status codes.
The differences that matter
1. Errors
This is the big one. fetch only rejects on network failure. A 404 or 500 is a "successful" fetch — you must check res.ok yourself. Forgetting this is a classic bug. (TypeError: Failed to fetch)
Axios rejects on any status outside 2xx, so a try/catch catches HTTP errors too.
2. JSON
fetch: JSON.stringify the body, set the Content-Type header, call res.json().
Axios: automatic both ways.
3. Timeouts
fetch has no timeout option, but AbortSignal.timeout() does it in one line:
await fetch(url, { signal: AbortSignal.timeout(5000) })
Axios: axios.get(url, { timeout: 5000 }).
4. Interceptors
Axios lets you run code on every request or response — add an auth header, refresh an expired token, log errors:
api.interceptors.request.use(config => {
config.headers.Authorization = `Bearer ${getToken()}`
return config
})
With fetch, you get the same effect by writing a small wrapper function.
5. Upload progress
Axios reports upload progress (onUploadProgress), handy for progress bars on file uploads. fetch can't report upload progress as simply. (Presigned URL uploads)
6. Size and dependencies
fetch is built in — zero kilobytes, zero dependencies. Axios adds a dependency (and, like any package, something to keep updated). (npm supply chain security)
Side by side
| fetch | Axios | |
|---|---|---|
| Install needed | No | Yes |
| Throws on 404/500 | No — check res.ok |
Yes |
| JSON handling | Manual | Automatic |
| Timeout | AbortSignal.timeout() |
timeout option |
| Interceptors | Write a wrapper | Built in |
| Upload progress | Awkward | Built in |
| Works in Next.js server caching | Yes (Next extends fetch) | No |
That last row matters if you use Next.js: its data caching and revalidation are built around fetch. (Next.js caching and server components)
A small fetch wrapper gets you most of Axios
export async function api(path, { body, ...options } = {}) {
const res = await fetch(`/api${path}`, {
...options,
headers: { 'Content-Type': 'application/json', ...options.headers },
body: body ? JSON.stringify(body) : undefined,
signal: options.signal ?? AbortSignal.timeout(10000),
})
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`)
return res.status === 204 ? null : res.json()
}
Which should you use?
- New project, especially Next.js: fetch (plus a small wrapper).
- You need upload progress or lots of interceptor logic: Axios is convenient.
- Existing project using Axios: no reason to rip it out.
- For data fetching in React components, consider a library like TanStack Query on top of either. (TanStack Query vs useEffect)
EasySpawn runs your frontend and API side by side on one server, so requests stay same-origin and there are no CORS headaches to fetch or Axios around. See how it works or join the waitlist.
Related: JavaScript Promises Explained · Async/Await in JavaScript · TypeError: Failed to Fetch · CORS Errors Explained
Keep reading
Webpack vs Vite: What a Bundler Does and Which One to Use
Bundlers turn your source files into the optimised JavaScript and CSS a browser loads. How Webpack and Vite differ — bundling everything up front vs serving native modules in development — speed, configuration, migration, and where Turbopack and Rspack fit.
Tauri vs Electron: Building Desktop Apps With Web Technology
Electron and Tauri both turn a web app into a desktop app for Windows, macOS and Linux. Electron bundles Chromium and Node.js; Tauri uses the system's web view and a Rust back end. How they compare on app size, memory, consistency, security, mobile support and learning curve.