cURL Builder: From Visual Interface to Command Line Generation
cURL Builder: From Visual Interface to Command Line Generation#
Often need to send curl commands to backend colleagues for API debugging. Writing them by hand means dealing with quote escaping and parameter concatenation—easy to make mistakes. Built a visual builder to generate complete commands with a few clicks.
Breaking Down cURL Command Structure#
A complete curl command has these parts:
curl -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer xxx' \
-d '{"name": "test"}' \
'https://api.example.com/users?page=1'
Breakdown:
- HTTP method:
-X POST/PUT/DELETE, GET can be omitted - Headers:
-H 'Key: Value', multiple allowed - Body:
-d 'data'or-F 'field=value'(form) - URL: Last parameter, with query params
Builder’s core is assembling these parts into valid command strings.
Quote Escaping: The Headache#
User input may contain single quotes, like password it's secret. Direct concatenation breaks command structure.
Solution: Escape single quotes with '\'' inside single-quoted strings:
function escapeSingleQuote(s: string): string {
return s.replace(/'/g, "'\\''")
}
How it works:
# Original: it's secret
# Escaped: 'it'\''s secret'
# Parse: 'it' + \' + 's secret'
Three parts concatenated: end previous quoted region, escaped quote, start new quoted region.
Handling URL Query Parameters#
User might input:
- Base URL:
https://api.example.com/users - Query params:
page=1,size=20
Need to assemble: https://api.example.com/users?page=1&size=20
Implementation:
function buildQueryString(params: KeyValue[]): string {
const active = params.filter(p => p.key.trim() !== '')
if (active.length === 0) return ''
return '?' + active
.map(p => `${encodeURIComponent(p.key)}=${encodeURIComponent(p.value)}`)
.join('&')
}
Note encodeURIComponent: param values may contain & or =, need encoding.
Three Body Modes#
1. Raw Mode#
Pass raw string, usually JSON:
curl -X POST -H 'Content-Type: application/json' \
-d '{"name": "test", "email": "test@example.com"}' \
https://api.example.com/users
Frontend uses textarea, add “Format JSON” button:
const handleFormatJson = () => {
try {
const parsed = JSON.parse(rawBody)
setRawBody(JSON.stringify(parsed, null, 2))
} catch {
// Ignore invalid JSON
}
}
2. form-data Mode#
Multipart form, use -F parameter:
curl -X POST \
-F 'username=admin' \
-F 'password=secret' \
https://api.example.com/login
Each field gets its own -F, supports file upload (-F 'file=@path').
3. x-www-form-urlencoded Mode#
URL-encoded form, use -d parameter:
curl -X POST \
-d 'username=admin&password=secret' \
https://api.example.com/login
All fields concatenated into one string, & separated, keys and values URL-encoded.
Generating Auth Headers#
Basic Auth#
Base64 encode username:password:
if (authType === 'basic' && authUsername) {
const cred = btoa(`${authUsername}:${authPassword}`)
parts.push(`-H 'Authorization: Basic ${cred}'`)
}
btoa is browser’s built-in Base64 encoding function.
Bearer Token#
Direct concatenation:
if (authType === 'bearer' && authToken) {
parts.push(`-H 'Authorization: Bearer ${authToken}'`)
}
Common Option Mapping#
cURL has many options, picking the useful ones:
| Option | Meaning | Use Case |
|---|---|---|
-i |
Include response headers | View status code and headers |
-v |
Verbose output | Debug connection issues |
-L |
Follow redirects | Short link redirects |
-k |
Ignore SSL cert | Self-signed cert testing |
-s |
Silent mode | Only get response body |
--compressed |
Request compression | Large response bodies |
-o file |
Output to file | Download files |
--max-time N |
Timeout seconds | Prevent hanging |
Frontend uses checkbox controls:
if (opts.includeHeaders) parts.push('-i')
if (opts.verbose) parts.push('-v')
if (opts.followRedirects) parts.push('-L')
if (opts.insecureSsl) parts.push('-k')
URL Quote Handling#
URL may contain special characters (&, space, *), need to decide whether to quote:
const needsQuoting = /[&\s'"()[\]{}*$|^<>`\\]/.test(fullUrl)
const urlStr = needsQuoting
? `'${escapeSingleQuote(fullUrl)}'`
: fullUrl
parts.push(urlStr)
Regex check: if contains these special characters, wrap in single quotes.
Quick Add Common Headers#
Preset common headers for one-click addition:
const COMMON_HEADERS = {
contentType: { key: 'Content-Type', value: 'application/json' },
authorization: { key: 'Authorization', value: 'Bearer ' },
accept: { key: 'Accept', value: 'application/json' },
userAgent: { key: 'User-Agent', value: 'curl/8.0' },
cookie: { key: 'Cookie', value: '' },
referer: { key: 'Referer', value: '' },
}
const addCommonHeader = (name: string) => {
const h = COMMON_HEADERS[name]
if (h) {
setHeaders(prev => [...prev, { id: nextId(), key: h.key, value: h.value }])
}
}
Dropdown selection auto-fills key-value.
State Management Structure#
Entire form in one state object:
const [url, setUrl] = useState('')
const [method, setMethod] = useState('GET')
const [headers, setHeaders] = useState<KeyValue[]>([])
const [queryParams, setQueryParams] = useState<KeyValue[]>([])
const [bodyMode, setBodyMode] = useState<'raw' | 'form-data' | 'x-www-form-urlencoded'>('raw')
const [rawBody, setRawBody] = useState('')
const [formFields, setFormFields] = useState<KeyValue[]>([])
const [authType, setAuthType] = useState<'none' | 'basic' | 'bearer'>('none')
// ... option states
KeyValue interface for dynamic lists:
interface KeyValue {
id: number
key: string
value: string
}
Add/remove/update operations:
const addHeader = () => {
setHeaders(prev => [...prev, { id: nextId(), key: '', value: '' }])
}
const removeHeader = (id: number) => {
setHeaders(prev => prev.filter(h => h.id !== id))
}
const updateHeader = (id: number, field: 'key' | 'value', val: string) => {
setHeaders(prev => prev.map(h => h.id === id ? { ...h, [field]: val } : h))
}
Command Generation Function#
Core is assembling all parts:
function buildCurlCommand(opts: Options): string {
const parts: string[] = ['curl']
// Method
if (opts.method !== 'GET') {
parts.push(`-X ${opts.method}`)
}
// Options
if (opts.includeHeaders) parts.push('-i')
if (opts.verbose) parts.push('-v')
// ...
// Headers
for (const h of opts.headers) {
if (h.key.trim()) {
parts.push(`-H '${escapeSingleQuote(h.key)}: ${escapeSingleQuote(h.value)}'`)
}
}
// Auth
if (opts.authType === 'basic' && opts.authUsername) {
const cred = btoa(`${opts.authUsername}:${opts.authPassword}`)
parts.push(`-H 'Authorization: Basic ${cred}'`)
}
// URL
const fullUrl = opts.url + buildQueryString(opts.queryParams)
parts.push(quoteUrl(fullUrl))
// Body
if (['POST', 'PUT', 'PATCH'].includes(opts.method)) {
if (opts.bodyMode === 'raw' && opts.rawBody.trim()) {
parts.push(`-d '${escapeSingleQuote(opts.rawBody)}'`)
} else if (opts.bodyMode === 'form-data') {
for (const f of opts.formFields) {
parts.push(`-F '${escapeSingleQuote(f.key)}=${escapeSingleQuote(f.value)}'`)
}
}
// ...
}
return parts.join(' ')
}
Cache with useMemo, depend on all states:
const curlCommand = useMemo(() => buildCurlCommand({
url, method, headers, queryParams, bodyMode, rawBody, formFields,
authType, authUsername, authPassword, authToken,
includeHeaders, verbose, followRedirects, insecureSsl, silent,
outputFile, compressed, maxTime,
}), [url, method, headers, /* ... */])
Final Result#
Built an online tool: cURL Builder
Features:
- 7 HTTP methods
- URL + query param assembly
- Three body modes
- Basic/Bearer auth
- Common options one-click toggle
- One-click copy command
Writing curl by hand is error-prone. Visual builder guarantees generated commands are always valid.
Related tools: cURL Converter | API Tester