Converting Curl Commands to JavaScript: fetch, axios, and node-fetch
Every API doc starts with a curl example. Every Stack Overflow HTTP answer uses curl. When you copy a request from browser DevTools, you get curl. But your application speaks JavaScript. Translating curl to fetch(), axios, or node-fetch by hand is tedious and error-prone — especially for requests with headers, authentication, and JSON bodies.
This guide covers the most common curl patterns and their JavaScript equivalents, plus the subtle gotchas that trip people up.
Simple GET Request
# Curl
curl https://api.example.com/users
# Fetch
const response = await fetch('https://api.example.com/users');
const data = await response.json();
# Axios
const { data } = await axios.get('https://api.example.com/users');
The simplest case. Fetch and axios both default to GET when no method is specified.
POST with JSON Body
# Curl
curl -X POST https://api.example.com/users \
-H 'Content-Type: application/json' \
-d '{"name":"Alice","email":"alice@example.com"}'
# Fetch
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' }),
});
const data = await response.json();
# Axios (auto-serializes objects to JSON)
const { data } = await axios.post('https://api.example.com/users', {
name: 'Alice',
email: 'alice@example.com',
});
Key difference: Axios automatically serializes objects to JSON and sets the Content-Type header. With fetch, you must do both manually.
Bearer Token Authentication
# Curl
curl https://api.example.com/me \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...'
# Fetch
const response = await fetch('https://api.example.com/me', {
headers: {
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9...',
},
});
# Axios
const { data } = await axios.get('https://api.example.com/me', {
headers: {
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9...',
},
});
Basic Authentication
# Curl
curl -u username:password https://api.example.com/data
# Fetch (must Base64 encode manually)
const response = await fetch('https://api.example.com/data', {
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
},
});
# Axios (has built-in auth option)
const { data } = await axios.get('https://api.example.com/data', {
auth: {
username: 'username',
password: 'password',
},
});
Curl's -u flag automatically Base64-encodes the credentials. With fetch, you need btoa(). Axios provides a cleaner auth option.
Multiple Headers
# Curl
curl https://api.example.com/data \
-H 'Authorization: Bearer token123' \
-H 'Accept: application/json' \
-H 'X-Request-ID: abc-123' \
-H 'Cache-Control: no-cache'
# Fetch
const response = await fetch('https://api.example.com/data', {
headers: {
'Authorization': 'Bearer token123',
'Accept': 'application/json',
'X-Request-ID': 'abc-123',
'Cache-Control': 'no-cache',
},
});
# Axios
const { data } = await axios.get('https://api.example.com/data', {
headers: {
'Authorization': 'Bearer token123',
'Accept': 'application/json',
'X-Request-ID': 'abc-123',
'Cache-Control': 'no-cache',
},
});
Form Data (URL-Encoded)
# Curl
curl -X POST https://api.example.com/login \
-d 'username=alice&password=secret123'
# Fetch
const response = await fetch('https://api.example.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
username: 'alice',
password: 'secret123',
}),
});
# Axios
const { data } = await axios.post(
'https://api.example.com/login',
new URLSearchParams({ username: 'alice', password: 'secret123' }),
);
When curl's -d flag is used without a Content-Type header, it defaults to application/x-www-form-urlencoded. The JavaScript URLSearchParams API handles encoding automatically.
Cookies
# Curl
curl https://api.example.com/dashboard \
-b 'session=abc123; theme=dark'
# Fetch (browser — cookies sent automatically for same-origin)
const response = await fetch('https://api.example.com/dashboard', {
credentials: 'include', // Needed for cross-origin cookies
});
# Fetch (manual cookie header — for Node.js)
const response = await fetch('https://api.example.com/dashboard', {
headers: {
'Cookie': 'session=abc123; theme=dark',
},
});
Important: In browsers, you generally don't set Cookie headers manually — the browser handles them. Set credentials: 'include' for cross-origin requests. In Node.js, you'll need to manage cookies manually or use a cookie jar library.
Common Gotchas
Fetch Doesn't Throw on HTTP Errors
# This WON'T throw on 404 or 500 responses:
const response = await fetch('/api/data');
const data = await response.json(); // May parse an error body
# Always check response.ok:
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('HTTP ' + response.status + ': ' + response.statusText);
}
const data = await response.json();
# Axios throws automatically on non-2xx status codes
Curl's -d Implies POST
# Curl: -d without -X automatically uses POST
curl https://api.example.com/data -d '{"key":"value"}'
# Many developers forget that -d implies POST in curl,
# and manually add -X POST, which is redundant
JSON.stringify vs String Body
# If curl sends a JSON string:
curl -d '{"name":"Alice"}' ...
# In fetch, you need JSON.stringify for objects:
body: JSON.stringify({ name: 'Alice' })
# But if you already have a JSON string, just pass it directly:
body: '{"name":"Alice"}'
Timeout Handling
# Curl has built-in timeout:
curl --max-time 10 https://api.example.com/slow
# Fetch uses AbortController:
const controller = new AbortController();
setTimeout(() => controller.abort(), 10000);
const response = await fetch('https://api.example.com/slow', {
signal: controller.signal,
});
# Axios has a simple timeout option:
const { data } = await axios.get('https://api.example.com/slow', {
timeout: 10000,
});
Copying Curl from Browser DevTools
The most common real-world workflow:
- Open Chrome/Firefox DevTools (F12)
- Go to the Network tab
- Perform the action that triggers the request
- Right-click the request → Copy → Copy as cURL (bash)
- Paste into our Curl to JavaScript Converter
Browser-copied curl commands include all headers the browser sent automatically (User-Agent, Accept-Language, sec-ch-ua, etc.). You'll usually want to strip these down to just the headers your application actually needs.
Which JavaScript HTTP Client to Use?
| Client | Best For | Bundle Size |
|---|---|---|
fetch() | Browser apps, minimal deps | 0 KB (built-in) |
axios | Full-featured apps, interceptors | ~13 KB gzipped |
node-fetch | Node.js < 18, fetch-like API | ~8 KB |
Node.js built-in fetch | Node.js ≥ 18 | 0 KB (built-in) |
For new projects: use native fetch() in both browser and Node.js 18+. Add axios only if you need interceptors, automatic retries, or request/response transformation middleware.
Convert any curl command instantly with our Curl to Fetch/Axios Converter — paste a curl command, pick your target format, and get clean JavaScript code.