Skip to content
koducathome

How to Convert cURL Commands to JavaScript fetch and Axios

Translate curl flags like -X, -H, -d, --json, -F and -u into JavaScript fetch and Axios code, with the defaults and gotchas that make conversions go wrong.

By the koducat teamUpdated 6 min read

Why convert curl commands

API documentation almost always shows examples as curl commands, and browser developer tools can copy any network request as curl. That makes curl the common language for describing an HTTP request. Turning one into working JavaScript is mostly mechanical, but a handful of curl defaults are easy to miss — and those are exactly the cases where the converted code silently sends a different request.

The fastest route is to paste the command into cURL to Code, which parses the flags and generates fetch or Axios code. The rest of this guide explains what each flag maps to, so you can check the output and convert by hand when you need to.

The curl flags you need to know

curl flagWhat it doesfetch equivalent
-X POSTSets the methodmethod: "POST"
-H 'Name: value'Adds a request headerheaders: { Name: "value" }
-d / --dataSends a body; implies POST and form encodingbody + Content-Type: application/x-www-form-urlencoded
--data-rawLike -d but @ is not treated as a filebody
--data-urlencodeURL-encodes the value before sendingnew URLSearchParams(...)
--jsonSends JSON; sets Content-Type and Accept to application/json; implies POSTbody: JSON.stringify(...) + both headers
-F / --formSends multipart/form-data; implies POSTbody: formData (no manual Content-Type)
-u user:passHTTP Basic authenticationAuthorization: Basic <base64>
-GMoves -d data into the query string and uses GETBuild the URL with URLSearchParams
-LFollows redirectsDefault in fetch (redirect: "follow")
-b 'a=1'Sends a Cookie headerBrowser: credentials: "include"; Node: Cookie header
-kSkips TLS certificate checksNo browser equivalent — fix the certificate

Most short flags have a long form: -H is --header, -X is --request and -d is --data. Repeating -d joins the values with &, so several -d flags produce one form-encoded body, not several requests. A value starting with @ is read from a file — -d @body.json sends the file's contents, with carriage returns and newlines stripped (--data-binary @body.json sends it byte for byte). When converting, load that file in your code or import it as a module.

-d changes two things at once: it switches the method to POST and sets Content-Type: application/x-www-form-urlencoded. A command like curl -d '{"a":1}' URL therefore sends JSON text labelled as a form. Many servers reject or misparse that, so add -H 'Content-Type: application/json' or use --json (available since curl 7.82.0).

Converting a command step by step

Take this typical documentation example:

curl -X POST https://api.example.com/v1/orders \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sku":"A-100","quantity":2}'
  1. URL: the first argument that is not a flag — https://api.example.com/v1/orders.
  2. Method: -X POST. Even without it, -d would have made this a POST.
  3. Headers: one entry per -H. Replace shell variables such as $TOKEN with a value from your configuration — never hard-code secrets.
  4. Body: the -d string. Because Content-Type is JSON, build it from an object with JSON.stringify rather than copying the string.
  5. Response handling: curl prints the body whatever the status; in code you must decide what to do with errors.
fetch
const res = await fetch("https://api.example.com/v1/orders", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ sku: "A-100", quantity: 2 }),
});

if (!res.ok) throw new Error(`Request failed: ${res.status}`);
const order = await res.json();
Axios
import axios from "axios";

const { data: order } = await axios.post(
  "https://api.example.com/v1/orders",
  { sku: "A-100", quantity: 2 }, // serialised as JSON automatically
  { headers: { Authorization: `Bearer ${process.env.API_TOKEN}` } },
);

Forms, Basic auth, query strings and file uploads

Form-encoded bodies (-d 'a=1&b=2' or repeated --data-urlencode) map to URLSearchParams. Passing a URLSearchParams object as the body makes fetch set the form Content-Type for you:

// curl -d "grant_type=client_credentials" -u "$CLIENT_ID:$CLIENT_SECRET" https://auth.example.com/token
const credentials = btoa(`${clientId}:${clientSecret}`); // Node: Buffer.from(...).toString("base64")

const res = await fetch("https://auth.example.com/token", {
  method: "POST",
  headers: { Authorization: `Basic ${credentials}` },
  body: new URLSearchParams({ grant_type: "client_credentials" }),
});

With -G, curl appends the data to the URL instead of sending a body. Build the query string explicitly so values are encoded correctly (the URL Encoder shows what encoding does to special characters):

// curl -G https://api.example.com/search --data-urlencode "q=rock & roll" -d "limit=10"
const url = new URL("https://api.example.com/search");
url.search = new URLSearchParams({ q: "rock & roll", limit: "10" }).toString();
const res = await fetch(url);

Multipart uploads (-F) map to FormData. Do not set the Content-Type header yourself — the runtime must add the multipart boundary:

// curl -F "title=Report" -F "file=@report.pdf" https://api.example.com/uploads
const form = new FormData();
form.append("title", "Report");
form.append("file", fileInput.files[0]); // a File or Blob

await fetch("https://api.example.com/uploads", { method: "POST", body: form });

fetch vs Axios: behaviour differences

BehaviourfetchAxios
HTTP 4xx/5xxResolves normally; check res.okRejects with an error by default
JSON request bodyYou call JSON.stringify and set Content-TypePlain objects are serialised as JSON automatically
JSON responseCall await res.json()Parsed into response.data
TimeoutsUse AbortSignal.timeout(ms)timeout option
AvailabilityBuilt into browsers and Node.js 18+Separate dependency

Either is fine. fetch avoids a dependency; Axios gives you interceptors and automatic JSON handling. What matters is that the converted code checks for errors, because curl's habit of printing any response hides that decision.

Timeouts deserve a specific mention. curl waits as long as it takes unless you pass --max-time or --connect-timeout, and fetch has no default timeout either. In converted code, pass signal: AbortSignal.timeout(10_000) to fetch, or timeout: 10_000 to Axios, so a hung server cannot block your process indefinitely. When the timeout fires, fetch rejects with a DOMException named TimeoutError, which you can tell apart from other network failures in a catch block.

Gotchas when converting

  • Shell quoting. Single quotes in bash stop variable expansion; double quotes allow it. A body copied from a command that used double quotes may contain escaped quotes (\") that are not part of the JSON.
  • Copied browser requests. "Copy as cURL" includes every header the browser sent, including cookies and headers like sec-ch-ua. Remove what the API does not need, and never commit cookies or tokens.
  • Forbidden headers. Browsers do not let JavaScript set some headers, such as Cookie, Host or Content-Length. In browser code, use credentials: "include" for cookies instead.
  • CORS. curl ignores CORS; browsers enforce it. Code that works from Node may fail in a browser for that reason alone — see How to Test a REST API.
  • `-k` / `--insecure`. There is no safe equivalent. Fix the certificate or, for local development only, trust the development CA on your machine.

More converters for code and data formats are listed under code converters.

Try it now with the free cURL to Code Converter — it runs in your browser.

Open cURL to Code

Frequently asked questions

Does curl -d send JSON?

It sends whatever string you give it, but labels it application/x-www-form-urlencoded unless you add a Content-Type header. Use --json or add -H 'Content-Type: application/json' when the body is JSON.

How do I convert curl -u to JavaScript?

Base64-encode username:password and send it as Authorization: Basic <encoded>. In browsers use btoa for ASCII credentials; in Node use Buffer.from(value).toString("base64").

Why does my converted fetch call not throw on a 404?

fetch only rejects on network failures. A 404 or 500 is a valid HTTP response, so you must check res.ok or res.status yourself. Axios, by contrast, rejects for status codes outside the 2xx range by default.

Can I use the converted code in Node.js?

Yes. Node.js 18 and later include a global fetch, FormData and URLSearchParams. Replace browser-only APIs such as btoa on non-ASCII input or file inputs with Node equivalents like Buffer and fs.