Developer
CURL TO FETCH
Paste a cURL command and get clean JavaScript fetch() or axios code. Handles headers, auth, JSON body, cookies, and all common flags.
// Paste a cURL command above to convert it.
Supports:
-X method · -H headers · -d / --data / --data-raw body · -u user:pass basic auth · -b cookie · --form / -F FormData · multiline with \
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
let currentTab = 'fetch';
let lastParsed = null;
// ---------- tokenizer ----------
function tokenize(str) {
const tokens = [];
let i = 0;
while (i < str.length) {
// skip whitespace
while (i < str.length && /\s/.test(str[i])) i++;
if (i >= str.length) break;
let tok = '';
if (str[i] === "'") {
i++;
while (i < str.length && str[i] !== "'") { tok += str[i++]; }
i++; // closing '
} else if (str[i] === '"') {
i++;
while (i < str.length && str[i] !== '"') {
if (str[i] === '\\' && i + 1 < str.length) { i++; tok += str[i++]; }
else tok += str[i++];
}
i++; // closing "
} else {
while (i < str.length && !/\s/.test(str[i])) {
if (str[i] === '\\' && i + 1 < str.length && str[i+1] === '\n') { i += 2; break; }
tok += str[i++];
}
}
if (tok) tokens.push(tok);
}
return tokens;
}
function parseCurl(raw) {
// normalize line continuations
const normalized = raw.replace(/\\\s*\n\s*/g, ' ').trim();
const tokens = tokenize(normalized);
if (!tokens.length || tokens[0].toLowerCase() !== 'curl') throw new Error('Input must start with "curl"');
const result = {
url: '',
method: 'GET',
headers: {},
body: null,
bodyType: null, // 'raw' | 'form' | 'formdata'
formFields: [],
auth: null,
};
let i = 1;
const eat = () => tokens[i++];
while (i < tokens.length) {
const tok = eat();
if (!tok) break;
if (tok === '-X' || tok === '--request') {
result.method = eat().toUpperCase();
} else if (tok.startsWith('-X')) {
result.method = tok.slice(2).toUpperCase();
} else if (tok === '-H' || tok === '--header') {
const hdr = eat();
const colon = hdr.indexOf(':');
if (colon > -1) {
const key = hdr.slice(0, colon).trim();
const val = hdr.slice(colon + 1).trim();
result.headers[key] = val;
}
} else if (tok.startsWith('--header=')) {
const hdr = tok.slice(9);
const colon = hdr.indexOf(':');
if (colon > -1) result.headers[hdr.slice(0,colon).trim()] = hdr.slice(colon+1).trim();
} else if (tok === '-d' || tok === '--data' || tok === '--data-raw' || tok === '--data-ascii' || tok === '--data-binary') {
result.body = eat();
result.bodyType = 'raw';
if (!result.method || result.method === 'GET') result.method = 'POST';
} else if (tok.startsWith('--data=') || tok.startsWith('--data-raw=')) {
result.body = tok.includes('=') ? tok.slice(tok.indexOf('=')+1) : eat();
result.bodyType = 'raw';
if (result.method === 'GET') result.method = 'POST';
} else if (tok === '-F' || tok === '--form') {
const field = eat();
result.formFields.push(field);
result.bodyType = 'formdata';
if (result.method === 'GET') result.method = 'POST';
} else if (tok === '--form-string') {
const field = eat();
result.formFields.push(field);
result.bodyType = 'formdata';
if (result.method === 'GET') result.method = 'POST';
} else if (tok === '-u' || tok === '--user') {
result.auth = eat();
} else if (tok.startsWith('--user=')) {
result.auth = tok.slice(7);
} else if (tok === '-b' || tok === '--cookie') {
result.headers['Cookie'] = eat();
} else if (tok === '--cookie-jar' || tok === '-c') {
eat(); // skip jar file arg
} else if (tok === '-o' || tok === '--output') {
eat(); // skip output file
} else if (tok === '--url') {
result.url = eat();
} else if (tok === '-L' || tok === '--location' || tok === '--compressed' ||
tok === '-s' || tok === '--silent' || tok === '-k' || tok === '--insecure' ||
tok === '-v' || tok === '--verbose' || tok === '-i' || tok === '--include' ||
tok === '--no-keepalive' || tok === '--http1.1' || tok === '--http2') {
// ignore
} else if (tok === '--connect-timeout' || tok === '--max-time' || tok === '-m') {
eat(); // skip value
} else if (tok === '--proxy' || tok === '-x') {
eat();
} else if (!tok.startsWith('-')) {
if (!result.url) result.url = tok;
}
}
if (!result.url) throw new Error('No URL found in cURL command');
// Basic auth → Authorization header
if (result.auth) {
result.headers['Authorization'] = 'Basic ' + btoa(result.auth);
}
// If body looks like JSON, ensure Content-Type
if (result.body && result.bodyType === 'raw' && !result.headers['Content-Type']) {
try { JSON.parse(result.body); result.headers['Content-Type'] = 'application/json'; } catch(e) {}
}
return result;
}
function indent(str, spaces) {
return str.split('\n').map(l => ' '.repeat(spaces) + l).join('\n');
}
function renderFetch(p) {
const hasOptions = p.method !== 'GET' || Object.keys(p.headers).length || p.body || p.formFields.length;
if (!hasOptions) return `const response = await fetch('${p.url}');\nconst data = await response.json();`;
let lines = [`const response = await fetch('${p.url}', {`];
if (p.method !== 'GET') lines.push(` method: '${p.method}',`);
const hkeys = Object.keys(p.headers);
if (hkeys.length) {
lines.push(' headers: {');
hkeys.forEach(k => lines.push(` '${k}': '${p.headers[k]}',`));
lines.push(' },');
}
if (p.bodyType === 'raw' && p.body) {
// detect JSON
try {
JSON.parse(p.body);
lines.push(` body: JSON.stringify(${p.body}),`);
} catch(e) {
lines.push(` body: '${p.body.replace(/'/g, "\\'")}',`);
}
} else if (p.bodyType === 'formdata' && p.formFields.length) {
lines.push(' body: (() => {');
lines.push(' const fd = new FormData();');
p.formFields.forEach(f => {
const eq = f.indexOf('=');
if (eq > -1) {
const k = f.slice(0, eq);
const v = f.slice(eq + 1);
lines.push(` fd.append('${k}', '${v}');`);
}
});
lines.push(' return fd;');
lines.push(' })(),');
}
lines.push('});');
lines.push('\nconst data = await response.json();');
return lines.join('\n');
}
function renderAxios(p) {
const config = {};
if (Object.keys(p.headers).length) config.headers = p.headers;
if (p.bodyType === 'raw' && p.body) {
try { config.data = JSON.parse(p.body); } catch(e) { config.data = p.body; }
} else if (p.bodyType === 'formdata' && p.formFields.length) {
// FormData note
}
const method = p.method.toLowerCase();
const urlStr = `'${p.url}'`;
const hasCfg = Object.keys(config).length > 0;
let cfgStr = '';
if (hasCfg) {
cfgStr = JSON.stringify(config, null, 2).replace(/"([^"]+)":/g, '$1:');
}
let call;
if (p.bodyType === 'formdata' && p.formFields.length) {
let lines = ['const fd = new FormData();'];
p.formFields.forEach(f => {
const eq = f.indexOf('=');
if (eq > -1) lines.push(`fd.append('${f.slice(0,eq)}', '${f.slice(eq+1)}');`);
});
lines.push('');
const hdrStr = Object.keys(p.headers).length ? `, { headers: ${JSON.stringify(p.headers, null, 2)} }` : '';
lines.push(`const { data } = await axios.${method}(${urlStr}, fd${hdrStr});`);
return lines.join('\n');
}
if (method === 'get' || method === 'delete' || method === 'head' || method === 'options') {
call = hasCfg ? `const { data } = await axios.${method}(${urlStr}, ${cfgStr});` : `const { data } = await axios.${method}(${urlStr});`;
} else {
const dataVal = config.data !== undefined ? JSON.stringify(config.data, null, 2) : 'null';
const restCfg = { ...config }; delete restCfg.data;
const restStr = Object.keys(restCfg).length ? `, ${JSON.stringify(restCfg, null, 2).replace(/"([^"]+)":/g, '$1:')}` : '';
call = `const { data } = await axios.${method}(${urlStr}, ${dataVal}${restStr});`;
}
return call;
}
function renderNodeFetch(p) {
let lines = ["const fetch = require('node-fetch');", ''];
const fetchCode = renderFetch(p);
lines.push('(async () => {');
fetchCode.split('\n').forEach(l => lines.push(' ' + l));
lines.push(' console.log(data);');
lines.push('})();');
return lines.join('\n');
}
function setOutput(code) {
document.getElementById('outputCode').textContent = code;
}
function convert() {
const raw = document.getElementById('curlInput').value.trim();
const errBar = document.getElementById('errorBar');
if (!raw) { setOutput('// Paste a cURL command above to convert it.'); errBar.style.display = 'none'; return; }
try {
lastParsed = parseCurl(raw);
errBar.style.display = 'none';
renderTab();
} catch(e) {
errBar.textContent = '⚠ ' + e.message;
errBar.style.display = 'block';
setOutput('// Fix the error above and try again.');
lastParsed = null;
}
}
function renderTab() {
if (!lastParsed) return;
if (currentTab === 'fetch') setOutput(renderFetch(lastParsed));
else if (currentTab === 'axios') setOutput(renderAxios(lastParsed));
else setOutput(renderNodeFetch(lastParsed));
}
function switchTab(name) {
currentTab = name;
document.querySelectorAll('.tab').forEach((t, i) => {
t.classList.toggle('active', ['fetch','axios','node'][i] === name);
});
renderTab();
}
function copyOutput() {
const code = document.getElementById('outputCode').textContent;
if (code.startsWith('//')) return;
navigator.clipboard.writeText(code).catch(() => {});
const btn = document.getElementById('copyBtn');
btn.textContent = 'copied!'; btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('copied'); }, 2000);
}