Skip to content

cURL to JavaScript fetch() Converter

Paste a cURL command and get async/await fetch() code — headers as a plain object, JSON bodies wrapped in a pretty JSON.stringify literal, ready for the browser or Node.js 18+.

Parsed 100% locally in your browser. 0 bytes sent to any server. Completely private.

Converting…

Examples
100% Client-Side: Tokens & credentials never leave your browser.

cURL command

Paste a cURL command — idiomatic code appears here instantly, converted entirely in your browser.

About this conversion

In this example, the cURL PUT command becomes an async JavaScript fetch() call. The verb maps to method: "PUT", each -H flag becomes an entry in the headers object, and the JSON body is embedded as a pretty-printed object literal inside JSON.stringify({...}) — so the snippet runs unchanged in every modern browser and Node.js 18+.

Key Syntax Transformations

  • async/await by default

    The output is a modern async function: const response = await fetch(url, { method, headers, body }); — no .then() pyramids, no dependencies.

  • JSON.stringify({...}) bodies

    JSON payloads are re-serialized as a formatted object literal wrapped in JSON.stringify — readable, editable, and byte-identical on the wire.

  • Basic auth pre-encoded

    -u credentials are base64-encoded at conversion time and emitted as an Authorization header — no btoa() or Buffer polyfill in the snippet.

  • response.json() consumption

    The snippet awaits response.json() and logs the parsed body, so it demonstrates the full request/response cycle as you would write it by hand.

Technical documentation

How cURL maps to fetch()

Every -H flag becomes an entry in the headers object, -X or --request sets the method property, and data flags become the body. When the Content-Type is application/json (or the body parses as JSON), the payload is embedded as a formatted object literal inside JSON.stringify — readable, diffable, and safe to edit. Form data keeps a plain string body with the urlencoded content type curl would send.

Basic auth from -u is converted to an Authorization: Basic header using base64 computed at conversion time, so the generated snippet has zero runtime dependencies — no btoa call, no Buffer polyfill. Credentials embedded in the URL are extracted into the same header automatically.

Quoting edge cases the converter handles

cURL commands copied from Chrome DevTools often arrive with $'…' ANSI-C quoting, escaped double quotes inside JSON bodies, and trailing backslashes joining multi-line commands. The tokenizer processes all of these the way bash would, then unescapes your body once — so '{"name": "John \"Doe\""}' arrives in the generated code exactly as curl would transmit it.

The parser also drops the Content-Length header (fetch computes it), merges repeated headers case-insensitively with the last value winning, and removes headers set with an empty value using curl's -H 'Header:' unset syntax.

Using the output in Node.js and TypeScript

The generated code runs in every modern browser and in Node.js 18+, where fetch is built in via undici. Top-level await needs an ES module context (type: module, or a .js file in a modern setup); otherwise wrap the snippet in an async function as shown.

In TypeScript the snippet type-checks as-is; the parsed JSON literal infers its type structurally. For stricter APIs, type response data with a generic helper such as response.json() as Promise<MyType> — the shape is already visible in the snippet.

fetch() versus curl behavior to know about

fetch follows redirects by default (like curl -L) and does not verify the same TLS options, so flags such as -k/--insecure have no equivalent and are ignored during conversion. Similarly, curl's --compressed becomes fetch's automatic decompression — nothing to configure.

One deliberate difference: a GET request carrying -d data is converted to query parameters, because the Fetch spec forbids GET bodies and browsers throw a TypeError. The wire request stays equivalent to what curl sent.

Frequently Asked Questions

Is my API token safe when converting cURL to fetch?

Yes. The conversion runs entirely in your browser — the command is never sent to a server, stored, or logged.

Can I run the generated fetch() code in Node.js?

Yes — Node.js 18 and later include fetch natively. Use an ES module for top-level await, or wrap the snippet in an async function for CommonJS projects.

What happens to a GET request with -d data?

The data is moved into the URL's query string, because the Fetch spec forbids GET bodies and browsers throw a TypeError — the wire request stays equivalent.

Why is Content-Length missing from the headers object?

fetch computes Content-Length itself; setting it manually is ignored or rejected. The converter drops it, along with headers unset via -H 'Header:'.

Does the converter support POST, PUT, PATCH, and DELETE?

Yes. The verb is taken from -X/--request, or inferred automatically: POST when a body flag like -d or --data-raw is present, HEAD for -I, and GET otherwise. Custom verbs pass through verbatim.

Why does the output use JSON.stringify instead of a raw string?

Wrapping a pretty-printed object literal in JSON.stringify keeps the snippet readable and editable while producing byte-identical request bodies. If the body is not valid JSON, the converter falls back to a plain string body.

How is basic auth (-u) converted?

The user:password pair is base64-encoded during conversion and emitted as an Authorization: Basic header, matching exactly what curl sends on the wire — with no runtime encoding code in the snippet.

What cURL flags are converted to fetch features?

-H becomes headers, -d/--data-raw/--data-binary become body, -X sets method, -u becomes an Authorization header, -G moves data into the query string, -A/-e/-b map to User-Agent, Referer, and Cookie headers, and --json sets both JSON content type and body.