YamlBox

Web & encoding · 5 min read

cURL to fetch: moving a request from terminal to browser

A cURL to fetch conversion is the usual route from an API’s documentation to working frontend code: the fastest way to understand an API is a cURL command copied from its docs, and the fastest way to ship it is `fetch`. Converting between them is mostly mechanical — flags map onto an options object — but two things routinely break the translation: body encodings and the browser security model that has no equivalent in a terminal.

Updated

How the flags map

  • `-X POST` → `method: "POST"` (or infer the method from `-d`).
  • `-H "Content-Type: application/json"` → an entry in `headers`.
  • `-d '{"a":1}'` → `body: JSON.stringify(...)`; cURL sends it form-encoded unless you say otherwise.
  • `--data-binary @file` → a `Blob`, `FormData` or `ArrayBuffer` body rather than a string.
  • `-u user:pass` → an `Authorization: Basic …` header built from the base64 of `user:pass`.
  • `-F name=@file` → `FormData` with a `File`, which is how file uploads are expressed.
                await fetch('https://api.example.com/v1/orders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer ' + token,
  },
  body: JSON.stringify({ sku: 'YB-1', qty: 2 }),
});
              

CORS: the part cURL never had to think about

cURL runs outside the browser’s security model, so it can call any host that answers. `fetch` in a page is bound by CORS: the server has to allow your origin, and for anything beyond simple requests it also has to answer a preflight `OPTIONS` correctly. A converted command that works perfectly in the terminal can fail in the browser with an opaque network error for exactly this reason — and no amount of header editing in the request will fix it, because the permission has to come from the server.

If a fetch fails with a CORS message, the fix is on the API or on your proxy — not in the JavaScript. Never work around it by putting an API key in client code to “avoid” the problem.

Credentials, redirects and streaming

Two more behaviours differ from the terminal. Cookies are not sent unless you pass `credentials: "include"`, and the server must respond with an explicit origin rather than a wildcard. Redirects are followed by default with `redirect: "follow"`, but a redirect loses the `Authorization` header across origins unless the server handles it, which is why authenticated calls often need `redirect: "manual"` and a deliberate follow. Streaming responses are read from `response.body` as a `ReadableStream`, where cURL simply prints as it receives.

Fetch or axios?

`fetch` is built in and works everywhere modern; axios adds convenience — automatic JSON parsing, interceptors, a readable error for non-2xx responses, upload progress in older runtimes. A converted cURL command is a reliable starting point for either: paste the generated options into an axios call and the headers and body carry over unchanged.

Convert without leaking

cURL commands copied from real sessions usually carry live bearer tokens, API keys or session cookies. A conversion that runs in the browser keeps those credentials on your machine, which is the only sensible way to work with a request you did not type yourself.

cURL to fetch — frequently asked questions

Why does my converted fetch request fail with a CORS error?

Because the browser enforces the same-origin policy and cURL does not. The API must send the appropriate `Access-Control-Allow-Origin` header and handle preflight requests. Proxying through your own backend is the standard fix when a third-party API does not allow browser origins.

How do I send form data with fetch instead of JSON?

Use a `FormData` object as the body and let the browser set the multipart boundary — do not set `Content-Type` yourself, because the value includes a boundary token the browser generates. This also covers file uploads from `-F` flags.

Does fetch send cookies by default?

No. Same-origin requests include cookies when `credentials: "same-origin"` applies, but cross-origin calls need `credentials: "include"`, and the server must respond with an explicit origin rather than a wildcard to let the response be read.

Tools mentioned in this post

Browse all 19 tools →

Keep reading

All 19 posts →