REST API Design Principles & Resource Modeling
Design clean, predictable REST APIs: Nouns vs verbs, HTTP methods, status code semantics, statelessness, idempotent mutations, and sub-resource nesting.
RESTful Resource Hierarchy & HTTP Method Semantics š
Resource-oriented URI hierarchy mapped to standard HTTP verbs and status codes.
01.1. Core Architectural Constraints of REST
Representational State Transfer (REST) was defined in 2000 by Roy Fielding in his doctoral dissertation. REST is not a rigid protocol or specification; it is an architectural style governed by six core constraints:
- Client-Server Architecture: Separates user interface concerns (clients) from data storage and business logic (servers), enabling independent evolution and scaling.
- Statelessness: Every request from client to server must contain all the contextual information needed to understand and process the request. The server must never store client session context in server memory between requests. Session state belongs entirely on the client (or in an external shared session store like Redis).
- Cacheability: Responses must explicitly declare themselves as cacheable or non-cacheable via HTTP headers (
Cache-Control,ETag,Expires) to eliminate redundant server round-trips. - Uniform Interface: The cornerstone of REST. Resources are identified in requests via standardized URIs; resource manipulation occurs via self-descriptive representations (JSON, XML); messages are self-describing with standard MIME types and status codes.
- Layered System: A client cannot tell whether it is connected directly to the end server or to an intermediary proxy, CDN, load balancer, or API gateway.
- Code-on-Demand (Optional): Servers can temporarily extend client functionality by transferring executable code (e.g., JavaScript).
02.2. Resource Modeling: Nouns, Plurals, Hierarchy & Filtering
Resource-oriented design models systems around nouns (entities) rather than verbs (remote procedure calls).
The Plural Noun Convention:
Endpoints should always represent collections of plural nouns:
GET /v1/customers(Retrieve list of customers)POST /v1/customers(Create new customer)GET /v1/customers/cus_89f0(Retrieve specific customer)PUT /v1/customers/cus_89f0(Full replacement of customer)PATCH /v1/customers/cus_89f0(Partial update of customer)DELETE /v1/customers/cus_89f0(Delete customer)
Sub-Resource Nesting (Max 2 Levels):
Nested endpoints express natural ownership and parent-child relationships:
GET /v1/customers/cus_89f0/orders(List orders belonging to customercus_89f0)POST /v1/customers/cus_89f0/orders(Create an order for customercus_89f0)
[!WARNING] Anti-Pattern (Deep Nesting): Avoid nesting beyond two levels (e.g.,
/orgs/1/depts/2/teams/3/projects/4/tasks/5). Deeply nested URLs become brittle, difficult to refactor, and tightly coupled. Flatten sub-resources to top-level endpoints once the child entity has a globally unique ID (e.g.,GET /v1/tasks/5).
Filtering, Sorting, and Field Selection:
Use query parameters to filter, sort, and paginate resource collections without creating ad-hoc URL paths:
httpGET /v1/orders?status=fulfilled&created_at_gte=2026-01-01&sort=-total_amount&limit=25&fields=id,status,total_amount
03.3. HTTP Verbs & Semantic Method Guarantees
HTTP methods provide standardized semantics regarding safety and idempotency:
- Safe Method: A method that does not modify server state (
GET,HEAD,OPTIONS). Clients can call it repeatedly without side effects. - Idempotent Method: Making
Nidentical requests produces the exact same server state as making 1 request (GET,PUT,DELETE,HEAD,OPTIONS). - Non-Idempotent Method: Making
Nidentical requests createsNseparate side effects (POST,PATCHdepending on implementation).
| HTTP Verb | Path | Safe? | Idempotent? | Success Status | RFC Definition |
|---|---|---|---|---|---|
| GET | /v1/invoices/inv_123 | Yes | Yes | 200 OK | Reads resource representation |
| POST | /v1/invoices | No | No | 201 Created | Appends resource; returns Location header |
| PUT | /v1/invoices/inv_123 | No | Yes | 200 OK / 204 No Content | Replaces entire entity with payload |
| PATCH | /v1/invoices/inv_123 | No | No* | 200 OK | Applies partial delta (JSON Merge Patch RFC 7396) |
| DELETE | /v1/invoices/inv_123 | No | Yes | 204 No Content | Removes resource |
*Note: While PATCH is technically non-idempotent by RFC 5789 specification (e.g., incrementing a counter), in standard REST CRUD APIs, applying a static JSON patch ({"email": "new@example.com"}) is practically idempotent.
04.4. HTTP Status Code Semantics & RFC 9457 Error Payloads
Returning the exact HTTP status code communicates outcome semantics without forcing client parsers to inspect arbitrary JSON strings.
Essential Status Codes:
200 OK: Standard successful response forGET,PUT,PATCH.201 Created: Successful resource creation (POST). Must include theLocation: /v1/users/usr_981header.202 Accepted: Request received for asynchronous background processing; processing is not yet complete.204 No Content: Successful request with zero response body (DELETE, emptyPUT).304 Not Modified: ConditionalGETmatches client cache (If-None-Match/ETag).400 Bad Request: Malformed JSON syntax or schema failure.401 Unauthorized: Authentication missing or invalid (no valid token/credentials).403 Forbidden: Authenticated, but user lacks permission to access this resource.404 Not Found: Resource URI does not exist.409 Conflict: State conflict (e.g., unique constraint violation, concurrent edit collision).422 Unprocessable Entity: Valid JSON syntax, but violates business validation rules.429 Too Many Requests: Rate limit exceeded (should includeRetry-Afterheader).500 Internal Server Error: Unhandled backend exception.502 Bad Gateway/504 Gateway Timeout: Downstream microservice error or timeout.
Standardized Error Payloads (RFC 9457 Problem Details):
json{ "type": "https://api.example.com/errors/insufficient-funds", "title": "Insufficient Account Balance", "status": 422, "detail": "Account acct_981 has balance `12.50, but transfer requires`50.00.", "instance": "/v1/transfers/txn_4019", "invalid_params": [ { "name": "amount", "reason": "Amount exceeds available balance" } ] }
05.5. Production Anti-Patterns to Avoid
- Verb-Driven URIs (RPC Tunneling):
- ā
POST /api/deleteUser?id=42 - ā
POST /api/createUser - ā
DELETE /v1/users/42,POST /v1/users
- ā
- HTTP 200 OK with Error in Body:
- ā Returning
200 OKwith{"status": "error", "message": "User not found"}. This breaks client error interceptors, load balancer health checks, and CDN caching layers. - ā
Return
404 Not Foundwith RFC 9457 JSON body.
- ā Returning
- Leaking Database Schema Details:
- ā Exposing internal auto-increment primary keys (
id: 1042) or database column names directly (tbl_usr_pwd_hash). - ā
Expose opaque prefixed UUIDs/ULIDs (
id: "usr_01H12B...") and clean domain field names.
- ā Exposing internal auto-increment primary keys (
āļøArchitectural Trade-offs & Production Realities
Architectural Advantages
- Universal industry standard: supported by every language, browser, proxy, and CDN natively
- High cacheability: standard HTTP cache headers (ETag, Cache-Control) enable zero-latency edge caching
- Stateless and decoupleable: backend servers scale horizontally without shared session memory
- Self-descriptive and human-readable JSON tooling (Postman, Swagger/OpenAPI, curl)
Trade-offs & Constraints
- Over-fetching: clients receive entire resource representations even if they only need a single field
- Under-fetching (N+1 round-trips): complex dashboard screens require multiple sequential HTTP requests
- Higher bandwidth overhead compared to binary serialization protocols like gRPC/Protobuf
Stripe's REST API is widely considered the gold standard in API engineering: strict noun-based resource modeling, predictable prefixed IDs (`ch_xxx`, `cus_xxx`), comprehensive RFC-compliant error payloads, idempotency keys for safe retries, and backward-compatible date versioning.
šÆ Staff+ Engineering Takeaways
- REST represents entities as plural nouns manipulated via standardized HTTP verbs.
- Statelessness requires every request to carry all authentication and execution context.
- Never return HTTP 200 OK for errors; use accurate 4xx/5xx status codes with RFC 9457 schemas.
- Keep resource URI hierarchies shallow (<= 2 levels) and use query parameters for filtering and sorting.
Topic Knowledge Assessment š§
Step through 3 scenario questions to test your staff-level grasp.
Which of the following HTTP requests correctly implements a partial update to a user's billing address according to REST best practices?
How clear and staff-actionable was this system breakdown?