Consuming REST APIs · Lesson 14 of 30
Testing REST APIs with curl, Postman and HTTPie
Every API developer needs a fast way to send a request and see exactly what comes back. curl is on every machine and in every API's documentation; Postman (or Insomnia, Bruno) adds a GUI, saved requests and team collections; HTTPie is a friendlier command line. This lesson covers the options you'll use daily.
In this lesson
- The curl options that matter
- Sending JSON, headers and auth
- Postman collections, environments and tests
- HTTPie and copying requests from the browser
curl essentials
| Option | Does |
|---|---|
-i | include response headers |
-v | verbose: request headers, TLS handshake, everything |
-X PUT | set the method (POST is implied by -d) |
-H 'Name: value' | add a header |
-d '…' / --json '…' | send a body; --json also sets JSON Content-Type and Accept (curl 7.82+) |
-u user:pass | HTTP Basic authentication |
-s, -o file, -w '%{http_code}' | silent, save body to a file, print the status code |
-L | follow redirects |
Trying them on a real API
// server.js
import express from 'express';
const app = express();
app.use(express.json());
app.post('/api/tickets', (req, res) => {
const auth = req.get('Authorization') ?? '';
if (!auth.startsWith('Basic ')) return res.status(401).set('WWW-Authenticate', 'Basic realm="support"').json({ error: 'Authentication required' });
const [user] = Buffer.from(auth.slice(6), 'base64').toString().split(':');
res.status(201).location('/api/tickets/T-501').json({ id: 'T-501', createdBy: user, ...req.body });
});
app.get('/old-docs', (req, res) => res.redirect(301, '/api/docs'));
app.get('/api/docs', (req, res) => res.json({ docs: 'https://api.learnfrenzy.com/docs' }));
app.listen(process.env.PORT ?? 3000);
$ curl --json '{"subject":"Cannot log in"}' http://localhost:3000/api/tickets
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="support"
Content-Type: application/json; charset=utf-8
{
"error": "Authentication required"
}
$ curl -u asha:s3cret --json '{"subject":"Cannot log in","priority":"high"}' http://localhost:3000/api/tickets
HTTP/1.1 201 Created
Location: /api/tickets/T-501
Content-Type: application/json; charset=utf-8
{
"id": "T-501",
"createdBy": "asha",
"subject": "Cannot log in",
"priority": "high"
}
$ curl -L http://localhost:3000/old-docs
HTTP/1.1 301 Moved Permanently
Location: /api/docs
Vary: Accept
Content-Type: text/plain; charset=utf-8
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"docs": "https://api.learnfrenzy.com/docs"
}Note how -u became a Basic header that the server decoded, and -L followed the 301 to the new location. In scripts, curl -s -o /dev/null -w '%{http_code}\n' URL prints just the status code – handy for health checks.
Postman: collections, environments, tests
A well-organised Postman workspace
- Collection: Tasks API
- Auth – Log in (saves the token to a variable)
- Tasks – List, Create, Get, Update, Delete
- Environments
- Local – baseUrl = http://localhost:3000
- Staging – baseUrl = https://staging-api.example.com
- Write URLs as
{{baseUrl}}/api/tasksand switch environments instead of editing requests. - Set auth once on the collection; requests inherit it.
- Add tests in the "Scripts → Post-response" tab and run the whole collection (or with Newman in CI):
pm.test('created a task', () => {
pm.response.to.have.status(201);
const task = pm.response.json();
pm.expect(task.title).to.eql('Write docs');
pm.collectionVariables.set('taskId', task.id); // used by the next request: {{baseUrl}}/api/tasks/{{taskId}}
});
Most APIs publish an OpenAPI file (next chapters); Postman and Insomnia can import it to generate a collection with every endpoint.
HTTPie and browser tricks
# HTTPie: JSON by default, coloured output, readable syntax
http POST localhost:3000/api/tasks title="Write docs" priority:=2 Authorization:"Bearer $TOKEN"
# Browser DevTools → Network → right-click a request → Copy → Copy as cURL
# then paste it into a terminal to replay exactly what the browser sent
Keep secrets out of history and screenshots
Tokens typed into commands end up in shell history; Postman workspaces can be shared. Use environment variables (-H "Authorization: Bearer $TOKEN"), Postman's secret variable type, and rotate any key that leaks.
Practice
Try it yourself
Write a curl command that sends a PATCH with JSON
{"status":"done"}and a bearer token from$TOKENto/api/tasks/7, printing response headers.Show solution
curl -i -X PATCH http://localhost:3000/api/tasks/7 \ -H "Authorization: Bearer $TOKEN" \ --json '{"status":"done"}'
Quick check
Which curl option shows response headers?
-i (or -v for everything).
What does --json do?
Sends the body and sets JSON Content-Type and Accept headers.
How do you switch Postman requests between local and staging?
Use a {{baseUrl}} variable and switch environments.
Summary
- curl: -i, -v, -X, -H, --json, -u, -L, -w cover most debugging.
- Postman: collections, environments, inherited auth and tests; import OpenAPI specs.
- Copy requests from DevTools as cURL; keep tokens in variables, not commands.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.