REST as Roy Fielding defined it is a set of architectural constraints. REST as practised in industry is a much narrower convention: HTTP + JSON + resource-oriented URLs + sensible status codes. We focus on the practical version while flagging where the originals still help.
Resources, Not Endpoints
The core mental shift: think nouns. A REST API exposes resources, and the HTTP method tells the server what to do with them.
| Bad (verbs) | Good (nouns) |
|---|---|
| POST /createUser | POST /users |
| POST /deleteOrder?id=42 | DELETE /orders/42 |
| POST /getProducts | GET /products |
| POST /updateUserEmail | PATCH /users/42 |
URL Conventions
- Use plural collection names:
/users,/orders,/invoices. - Use lowercase and hyphens:
/payment-methods, not/PaymentMethods. - Identifiers in the path:
/users/42/orders/100. - Filtering, sorting, pagination in query strings:
/orders?status=open&limit=50. - Don't put format in the URL (
/users.json) — useAcceptheaders.
Resource Modeling
For each resource, decide:
- What does it represent?
- What identifies it (UUID, slug, integer)?
- What relationships does it have?
- What operations are valid on it?
Standard CRUD shape
GET /orders List
POST /orders Create
GET /orders/:id Read
PUT /orders/:id Replace
PATCH /orders/:id Partial update
DELETE /orders/:id Delete
Nested resources
Nest when the child cannot exist without the parent or is naturally scoped to it:
GET /users/42/orders Orders for user 42
GET /orders/100/line-items Line items for order 100
Don't nest beyond two levels; deep paths become brittle. If the relationship can be queried independently, prefer a top-level resource with a filter:
GET /orders?user_id=42&status=open
Actions That Don't Fit CRUD
Sometimes an operation is genuinely an action, not a CRUD on a resource. Two clean approaches:
Sub-resource as a noun
POST /orders/100/cancellation Cancel order 100
POST /users/42/password-reset Trigger reset
POST /jobs/9/runs Start a new run
Verb path as a last resort
POST /orders/100:cancel Action style (Google API style)
Either is fine if used consistently. Avoid mixing both in the same API.
Representations
A resource has one canonical representation that callers can fetch and modify:
{
"id": "ord_100",
"status": "open",
"amount": 4999,
"currency": "USD",
"created_at": "2025-01-15T12:00:00Z",
"customer_id": "cus_42",
"links": {
"self": "/orders/100",
"customer": "/customers/42"
}
}
- Use ISO-8601 timestamps in UTC.
- Use cents/integers for money, not floats.
- Use string enums for status, not magic numbers.
- Use prefixed IDs (
ord_,cus_) — Stripe-style — to make logs readable.
Bulk Operations
Single-item endpoints work to a point, then 1,000 sequential calls become impractical. Add bulk endpoints explicitly:
POST /orders/bulk
{ "orders": [ { ... }, { ... } ] }
→ 200 OK
{ "results": [
{ "status": 201, "id": "ord_a" },
{ "status": 422, "error": "..." }
]
}
Per-item status lets a partial failure not fail the whole batch. Cap batch size and document the limit.
Async Operations
If work takes more than a couple of seconds, return 202 with a job resource:
POST /reports
→ 202 Accepted
Location: /jobs/9
{ "job_id": "job_9", "status": "queued" }
GET /jobs/9
→ 200 OK
{ "status": "running", "progress": 0.6 }
GET /jobs/9
→ 200 OK
{ "status": "succeeded", "result": "/reports/77" }
Error Shapes
Errors should be machine-readable and consistent across the API:
{
"error": {
"type": "validation_error",
"message": "amount must be positive",
"field": "amount",
"request_id": "req_xyz"
}
}
RFC 7807 (problem+json) is the standardised version — adopt it if it fits, or document your shape clearly.
HATEOAS
Fielding's REST included hypermedia controls — links in responses telling clients what they can do next. In practice almost no industrial REST API does this fully. Most include a few useful links (self, related resources, next page) and let documentation cover the rest. That is fine.
Cert Mapping
| Cert | REST scope |
|---|---|
| AWS SAA | API Gateway REST APIs, integrations |
| AWS Data Engineer | API ingestion patterns |
Next we'll cover the practical patterns that make a REST list endpoint scale: filtering, sorting, and pagination.