Backend Transport
REST API: Architecture and Best Practices
Roy Fielding published his dissertation in 2000 describing REST as an architectural style for scalable distributed systems. Most APIs calling themselves REST violate at least three of his six constraints - and still work perfectly well. The question is which violations are critical and which are reasonable trade-offs.
- **Stripe API** - the gold standard of REST design: resource naming, Idempotency-Key for payments, URI versioning (/v1/), detailed status codes
- **GitHub REST API** - HATEOAS-like `_links` in responses, pagination via Link header, versioning via Accept header
- **Twitter/X API v2** - migration from RPC-style v1 (`/statuses/show.json`) to resource-based v2 (`/tweets/{id}`) as a breaking migration example
- **PayPal API** - one of the few public APIs with genuine HATEOAS, `_links` govern the payment flow
The 6 REST Constraints
In 2000, Roy Fielding published his doctoral dissertation describing REST as an architectural style for distributed hypermedia systems. Most APIs calling themselves REST violate at least three of his six constraints.
REST stands for **Representational State Transfer**. It is not a protocol or standard - a set of architectural constraints. HTTP is the de facto implementation, but REST is theoretically applicable to any protocol.
- **Uniform Interface** - a consistent way to interact with resources: resources identified by URI, manipulation through representations, self-descriptive messages
- **Stateless** - each request contains all information needed to process it; the server stores no client state between requests
- **Cacheable** - responses must explicitly declare whether they are cacheable (`Cache-Control`, `ETag`)
- **Client-Server** - separation of concerns: UI and data storage are decoupled and can evolve independently
- **Layered System** - the client has no way to tell whether it is connected directly to the end server or an intermediary
- **Code on Demand** (optional) - servers can send executable code to clients (JavaScript)
**Why Stateless is critical for scaling:** if a server stores session state in memory, each subsequent request from the same client must reach the same instance. With stateless design any instance handles any request - horizontal scaling without sticky sessions.
**REST != HTTP.** REST is an architectural style. HTTP is a protocol. REST describes how to build systems; HTTP describes how to transfer data. REST constraints can be violated over HTTP (stateful APIs) or followed over other protocols.
A client receives a JWT token on login and sends it in every request. Which REST constraint does this implement?
Resource Design and HTTP Methods
The most common mistake is naming endpoints as actions (`/getUser`, `/createOrder`, `/deleteItem`). In REST, a URI identifies a **resource**, not an action. The action is conveyed by the HTTP method.
| Method | Action | Idempotent | Safe | Example |
|---|---|---|---|---|
| GET | Retrieve resource | Yes | Yes | GET /users/123 |
| POST | Create resource | No | No | POST /orders |
| PUT | Replace resource entirely | Yes | No | PUT /users/123 |
| PATCH | Partial update | No* | No | PATCH /orders/456 |
| DELETE | Delete resource | Yes | No | DELETE /items/789 |
HTTP status codes are part of the REST language. Clients should understand the outcome of an operation without parsing the response body.
| Code | Meaning | When to use |
|---|---|---|
| 200 OK | Success with body | GET, PUT, PATCH - resource is returned |
| 201 Created | Created | POST - new resource created, include Location header |
| 204 No Content | Success without body | DELETE, PUT when body is not needed |
| 400 Bad Request | Client error | Invalid JSON, missing required fields |
| 401 Unauthorized | No authentication | Token absent or expired |
| 403 Forbidden | No permission | Token valid, but no access to resource |
| 404 Not Found | Resource not found | ID does not exist |
| 409 Conflict | State conflict | Duplicate email, uniqueness violation |
| 422 Unprocessable | Semantic error | JSON valid but data is incorrect |
| 500 Internal Error | Server error | Unexpected exception |
**Anti-pattern:** returning `200 OK` with `{"success": false, "error": "Not found"}`. This forces clients to parse the body to determine success - it defeats the purpose of HTTP status codes.
Which HTTP method is correct for 'add item to cart' when the cart already exists?
Idempotency and Safety
Networks are unreliable: a request may be sent but the response may be lost. The client does not know whether the operation was completed. The natural solution is to retry. But what if the server executes it twice?
Two independent properties of HTTP operations: **idempotency** (repeated calls produce the same result) and **safety** (no side effects).
| Method | Idempotent | Safe | Explanation |
|---|---|---|---|
| GET | Yes | Yes | Read-only - repeated GET returns the same data |
| HEAD | Yes | Yes | Like GET, but no body |
| OPTIONS | Yes | Yes | Returns supported methods |
| PUT | Yes | No | PUT /users/1 with the same data - result is the same |
| DELETE | Yes | No | DELETE twice: first deletes, second returns 404, state is the same |
| POST | No | No | POST /orders creates a new order each time |
| PATCH | No* | No | PATCH SET balance=100 is idempotent; PATCH balance+=10 is not |
**Stripe, Braintree, Adyen** - all major payment systems implement Idempotency-Key. The client generates a UUID before the request and passes it in a header. Servers store responses for 24-72 hours. This is the standard pattern for financial operations.
DELETE /users/42 is called twice. The first call deleted the user; the second returned 404. Is DELETE idempotent?
API Versioning
APIs change - fields are added, structures evolve, endpoints are removed. When clients (mobile apps, partners) cannot update instantly, a strategy for version coexistence is needed.
The key distinction: **breaking changes** require a new version; **non-breaking changes** do not.
| Breaking change | Non-breaking change |
|---|---|
| Removing a field from the response | Adding a new optional field |
| Renaming a field | Adding a new endpoint |
| Changing a field type (string to number) | Adding new HTTP methods |
| Changing the semantics of an existing field | Extending an enum with new values (carefully) |
| Changing the URL structure | Adding optional query parameters |
**Sunset strategy:** when releasing v2, do not delete v1 immediately. Add a `Sunset: Sat, 01 Jan 2026 00:00:00 GMT` header to v1 responses - clients can log it and plan migration accordingly.
In v1, the field `user.name` returns a string. The new version needs to split it into `firstName` and `lastName`. Is this a breaking change?
HATEOAS: Hypermedia Links in Responses
HATEOAS - Hypermedia As The Engine Of Application State - is the last and least commonly implemented REST constraint. The idea: a server response contains both data and links to the available next actions.
In practice, the vast majority of 'REST APIs' are RPC-over-HTTP: verbs in URLs or hardcoded routes with no hypermedia links. Fielding publicly criticized such APIs, stating they are 'not REST'.
**When HATEOAS is genuinely useful:** long-lived public APIs (PayPal API uses HATEOAS) where clients are third-party developers who should not hardcode URLs. For internal microservice APIs HATEOAS is typically overkill.
Any JSON API over HTTP is a REST API
REST is a set of 6 architectural constraints. Most HTTP APIs violate at least Stateless or Uniform Interface
The term REST became a marketing synonym for 'not SOAP'. Fielding wrote in 2008: 'REST APIs must be hypertext-driven', criticizing the misuse of the term
An API returns an order with `_links.pay` and `_links.cancel`. When status is 'shipped' these links are absent. What does this provide?
REST API: Key Ideas
- REST is Fielding's 6 architectural constraints from 2000; Stateless is critical for horizontal scaling
- URI identifies a resource (noun), HTTP method conveys the action; status codes carry semantics without body parsing
- Idempotency (GET/PUT/DELETE) enables safe retries; Idempotency-Key solves the POST duplication problem
- HATEOAS is rarely implemented but conceptually important: the server controls valid state transitions
Related Topics
REST is a synchronous interaction protocol that exists alongside other API design approaches:
- GraphQL — REST alternative: one endpoint, client defines response shape
- gRPC — Binary protocol over HTTP/2, alternative for microservices
- HTTP Fundamentals — Transport layer of REST: methods, headers, status codes
Вопросы для размышления
- How does idempotency differ from safety in HTTP methods, and why is DELETE idempotent despite modifying state?
- Under what conditions does HATEOAS add genuine value, and when does it become unnecessary complexity?
- How does the Stateless REST constraint influence the choice of authentication mechanism (sessions vs JWT)?