cURL Advanced
๐ cURL Advanced
Bearer Tokens & API Keys, in Practice
Chapter 6's basic auth example (-u) is fine for that specific auth style, but most real APIs use a bearer token or an API key instead. Both go in a header โ the practical question is where the token's actual value comes from.
An API key sometimes goes in a custom header (X-API-Key: ...) instead of Authorization โ check the API's documentation for which it expects. Either way, avoid putting a key in the URL as a query parameter (?api_key=...) if a header option exists: URLs routinely end up in server access logs, browser history, and proxy logs, all places a header's contents typically don't.
File Uploads: -F
Uploading a file needs multipart/form-data, a different body format from -d's JSON or form-urlencoded โ that's what -F is for. The @ prefix tells curl to read a field's value from a file on disk rather than treating it as literal text.
Mixing -F with -d in the same request doesn't work as expected โ they build genuinely different body formats, so a request is either -d's style or -F's multipart style, not both at once.
Piping to jq for Readable JSON
A raw JSON response prints as one dense, hard-to-read line. Piping it through jq โ already covered for general shell use in bash_intermediate_07 โ pretty-prints it, and can filter down to just the fields that matter.
The -s (silent) flag suppresses curl's progress meter, which would otherwise clutter the output being piped into jq.
Saving & Replaying Requests as Scripts
A command retyped (or found again in shell history) every time isn't much better than starting from scratch. Saving it as a small shell script makes it reusable and shareable โ the cURL equivalent of Chapter 5's .http files.
--data @product.json loads the body from a separate file rather than an inline string โ useful once a body gets long enough to be awkward as one quoted line. Both $BASE_URL and $TOKEN come from the environment, not hardcoded โ set once, reused by any script that needs them.
An Inline Command vs. a Saved Script
Inline, Retyped Each Time
Works once, then has to be found again in shell history or retyped from memory โ easy to introduce a small mistake re-copying a long command.
Saved as a Script
chmod +x create-product.sh && ./create-product.sh โ reusable, shareable with a teammate, and can be committed to the repo like any other script.
| Flag / Pattern | Purpose |
|---|---|
-F "field=@file" | Multipart file upload |
--data @file.json | Load the request body from a file instead of inline text |
-o file | Save the response body to a file instead of printing it |
| jq | Pretty-print and filter a JSON response |
$VAR in a script | Reads a value from the environment rather than hardcoding it |
Putting It Together: A Reusable Upload Script
Run as ./upload-avatar.sh photo.jpg โ $1 is the shell's first script argument, so the same script uploads any file passed to it, authenticates via an environment variable, and prints just the resulting URL, readably, via jq.
๐ป Coding Challenges
Challenge 1: Fix the Upload Command
A developer writes curl -X POST https://api.example.com/upload -d "file=@photo.jpg" and the file arrives corrupted/unusable server-side. What's wrong, and what's the fix?
Goal: Practice recognizing that -d and -F build genuinely different body formats, not interchangeable syntax for "attach a file."
Challenge 2: Filter a Response With jq
Given a response { "id": 42, "items": [{ "name": "Mouse", "price": 24.99 }, { "name": "Keyboard", "price": 59.99 }] }, write the jq filter to print just the array of item names.
Goal: Practice writing a jq filter over an array field, building on bash_intermediate_07's coverage.
Challenge 3: Make a Script Reusable
Rewrite this hardcoded command as a script using environment variables instead: curl -H "Authorization: Bearer abc123" https://api.example.com/orders, so the same script works for any base URL or token without editing the file.
Goal: Practice the environment-variable substitution pattern this chapter uses for reusable scripts.
Saving a script like Chapter 6/7's examples feels like good practice โ until the token inside it is a real, working credential, and the script gets committed to a shared Git repository. This is the exact same mistake as a hardcoded database connection string or a committed API key covered elsewhere on this site: once pushed, that token is compromised, regardless of whether the file is later edited or deleted. Every script in this chapter deliberately reads $TOKEN from the environment rather than embedding a literal value โ that's not a style preference, it's the difference between a script that's safe to commit and one that isn't.
๐ฏ What's Next
The next chapter is Browser-Based API Testing โ the simplest tool of all: URL query parameters, HTML forms, and reading real requests in the DevTools Network tab.