Request and Response Formats
REST is explicitly format-agnostic — the architecture says nothing about JSON, XML, or any specific data format. Instead, it relies on the Content-Type header (what format the body is in) and the Accept header (what format the client wants back) to negotiate representations.
Beyond JSON: Multiple Input Formats
If your API only accepts JSON request bodies, you've excluded standard HTML <form> elements — which means every form needs JavaScript just to submit data. Real-world APIs often handle multiple input formats:
| Content-Type | When It's Used |
|---|---|
application/json |
JavaScript clients, API-to-API calls |
application/x-www-form-urlencoded |
Standard HTML form submissions (the default for <form>) |
multipart/form-data |
File uploads and mixed data |
Content Negotiation: Multiple Output Formats
The "Representational" in REST means a resource can have multiple representations. The Accept header is how the client requests a specific format:
| Accept Header | Use Case |
|---|---|
application/json |
JavaScript clients, SPAs, mobile apps |
text/html |
Server-side rendering, HTMX, progressive enhancement |
text/csv |
Data exports, spreadsheet-friendly downloads |
application/xml |
Legacy systems, enterprise integrations |
Public vs Private APIs
The design considerations change dramatically based on who's consuming your API:
| Aspect | Private API | Public API |
|---|---|---|
| Consumers | Your own front-end team | External developers worldwide |
| Breaking changes | Coordinate with your team | You can't — people depend on the old behavior |
| Versioning | Often unnecessary | Essential: /v1/api/books, /v2/api/books |
| Documentation | Team Wiki, Slack | Published docs, OpenAPI spec, changelogs |
| HATEOAS | Rarely needed | Valuable for discoverability |
Versioning Strategies
URL path versioning is the most visible and widely used approach. Once published, don't change existing behavior — add new versions instead.
HTTP Method Challenges
In theory, REST cleanly maps to HTTP methods. In practice, there are complications:
- Corporate proxies may strip or block PUT and DELETE requests
- Firewalls sometimes only allow GET and POST
- HTML forms only support GET and POST natively
- CORS preflight adds an OPTIONS request before non-simple methods
| Problem | Solution |
|---|---|
| Proxy strips PUT/DELETE | Use HTTPS (proxies can't inspect encrypted traffic) |
| Firewall blocks methods | POST with X-HTTP-Method-Override: PUT header |
| HTML form limitation | Use JavaScript fetch() or hidden field _method=PUT |
| CORS preflight overhead | Handle OPTIONS in your API (return allowed methods) |
API Mocking and json-server
You don't always need to build a backend before building a frontend. API mocking lets you prototype quickly. json-server generates a full CRUD REST API from a single JSON file:
Given a db.json with {"books": [...]}, json-server automatically creates GET/POST/PUT/PATCH/DELETE endpoints at /books and /books/:id. It even supports filtering, pagination, and sorting. Changes persist back to the JSON file.
OpenAPI (Swagger)
The OpenAPI Specification (OAS) is a standard, language-agnostic format for describing REST APIs. Because it's machine-readable, tools can automatically generate documentation, client SDKs, server stubs, and tests from it.
| Tool | What It Does |
|---|---|
| Swagger UI | Renders your spec as interactive HTML documentation |
| Swagger Editor | Browser-based editor with real-time validation and preview |
| OpenAPI Generator | Generate client SDKs and server stubs from your spec |
API Testing
APIs are contracts — they promise that given certain inputs, they'll produce certain outputs. Testing verifies that the contract holds.
Testing Tools
| Tool | Type | Best For |
|---|---|---|
curl |
CLI | Quick testing, CI pipelines, everywhere |
HTTPie |
CLI | Human-friendly syntax, colored output |
| Postman / Insomnia | GUI | Exploring APIs, team collaboration, saved collections |
| Browser DevTools | Built-in | Inspecting live traffic, "Copy as curl" |
| supertest (Node.js) | Automated | CI/CD, regression testing, test suites |
What to Test
- Happy path — correct input produces expected output
- Error cases — missing fields, invalid IDs, wrong data types
- Status codes — verify 201 on create, 400 on bad input, 404 on missing
- Response shape — correct JSON structure, expected fields present
- Idempotency — can you safely retry the same request? (GET and PUT should be safe)
Beyond REST
REST is the dominant architectural style, but it's not perfect for every situation:
| Problem | Why REST Struggles |
|---|---|
| Batch operations | Update 100 items at once? REST says make 100 PUT requests. |
| Complex queries | Filter by 10 fields, aggregate, join across resources? Query strings get unwieldy. |
| Over/under-fetching | GET returns all 50 fields when you need 3, or you need 4 requests to render one page. |
| Non-CRUD actions | "Send email", "run report" — these are actions, not resources. |
Alternatives Comparison
| Aspect | REST | GraphQL | gRPC |
|---|---|---|---|
| Endpoints | Many (/users, /posts) |
One (/graphql) |
Per service/method |
| Data format | JSON (text) | JSON (text) | Protocol Buffers (binary) |
| Best for | CRUD, public APIs, simple apps | Complex UIs, mobile apps | Service-to-service, streaming |
| HTTP caching | Works great (GET is cacheable) | Custom caching needed | Not typically used |
| Learning curve | Low | Medium | Higher |
| Browser support | Native (fetch) | Native (it's just POST) | Needs proxy (gRPC-Web) |