Skip to content
5 min read·Lesson 3 of 10

REST Principles and Resource Modeling

How REST really works: resources, representations, uniform interface, and the patterns that make a REST API feel right.

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 /createUserPOST /users
POST /deleteOrder?id=42DELETE /orders/42
POST /getProductsGET /products
POST /updateUserEmailPATCH /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) — use Accept headers.

Resource Modeling

For each resource, decide:

  1. What does it represent?
  2. What identifies it (UUID, slug, integer)?
  3. What relationships does it have?
  4. 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

CertREST scope
AWS SAAAPI Gateway REST APIs, integrations
AWS Data EngineerAPI ingestion patterns

Next we'll cover the practical patterns that make a REST list endpoint scale: filtering, sorting, and pagination.

Key Takeaways

  • REST is about resources, not endpoints — model nouns, not verbs.
  • Use plural collection URLs and consistent identifiers.
  • Nesting expresses ownership; flatten when relationships are independent.
  • Bulk operations and async work need explicit, predictable shapes.
  • HATEOAS is rare in practice; consistency and documentation matter more.

Test your knowledge

Try exam-style practice questions to reinforce what you've learned.

Practice Questions →