← Back to All Guides

Migrating from cURL to Fetch API: The Complete Developer Guide

Whenever third-party API providers (like Stripe, OpenAI, GitHub, or Twilio) document their REST endpoints, their code samples almost always lead with a terminal cURL command.

While cURL is ideal for quick command-line verification, frontend developers and Node.js engineers must translate these CLI flags into modern, idiomatic JavaScript using the Fetch API. In this guide, we map common cURL parameters to Fetch options, explain body serialization, and implement modern timeout patterns with AbortSignal.

1. Mapping cURL Flags to Fetch Options

cURL Flag Fetch Equivalent Example Usage
-X, --request method: 'POST' Specifies HTTP verb (GET, POST, PUT, DELETE)
-H, --header headers: { ... } Sets custom request headers (Authorization, Content-Type)
-d, --data body: JSON.stringify(...) Request body payload
-u, --user headers: { 'Authorization': 'Basic ...' } Base64-encoded username and password

2. Translating a Complete Real-World Example

Consider this standard terminal cURL command sending a JSON payload with a Bearer authentication token:

curl -X POST https://api.example.com/v1/checkout \ -H "Authorization: Bearer sk_live_987654321" \ -H "Content-Type: application/json" \ -d '{"plan":"pro","seats":5}'

The Idiomatic JavaScript Fetch Implementation

async function createCheckout() { try { const response = await fetch('https://api.example.com/v1/checkout', { method: 'POST', headers: { 'Authorization': 'Bearer sk_live_987654321', 'Content-Type': 'application/json' }, body: JSON.stringify({ plan: 'pro', seats: 5 }), // Modern timeout handling (aborts after 8 seconds) signal: AbortSignal.timeout(8000) }); // Note: fetch() does NOT throw on 4xx or 5xx status codes! if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); console.log('Success:', data); return data; } catch (error) { if (error.name === 'TimeoutError') { console.error('Request timed out after 8 seconds.'); } else { console.error('Fetch failed:', error.message); } } }
Crucial Fetch Caveat: Unlike libraries like Axios, standard fetch() only rejects a Promise on network failure or if the request was aborted. A 404 Not Found or 500 Server Error is still considered a resolved Promise. Always verify response.ok!

3. Handling FormData and File Uploads

When translating cURL multipart uploads (e.g. curl -F "file=@photo.jpg"), never manually set the Content-Type header. The browser must set it automatically along with the dynamic multipart boundary:

const formData = new FormData(); formData.append('file', fileBlob, 'photo.jpg'); const response = await fetch('/api/upload', { method: 'POST', // Do NOT set 'Content-Type': 'multipart/form-data' manually! body: formData });

4. Try Our Free Client-Side cURL to Fetch Converter

Want to paste raw cURL commands and instantly get clean, copy-pasteable JavaScript code blocks without uploading your API keys?

🚀 Open cURL to Fetch Converter