0

REST — Representational State Transfer, what it actually constrains

Intermediate5 min read·eng-13-004
interview

Concept

REST (Representational State Transfer) — an architectural style for distributed hypermedia systems, defined by Roy Fielding in his 2000 doctoral dissertation. NOT a standard, NOT a protocol — a set of constraints.

Fielding's 6 constraints (these are the actual definition of REST):

  1. Client-server: Client and server are separate. Client doesn't concern itself with data storage; server doesn't concern itself with UI.
  2. Stateless: Each request from client to server must contain ALL information needed to understand the request. The server stores NO session state. State, if any, lives on the client.
  3. Cacheable: Responses must define themselves as cacheable or non-cacheable. Clients can reuse cached responses.
  4. Uniform interface: The key constraint. 4 sub-constraints:
    • Resource identification in requests (URLs identify resources).
    • Resource manipulation through representations (client holds a representation and uses it to manipulate the resource).
    • Self-descriptive messages (each message includes enough info to describe how to process it).
    • HATEOAS — Hypermedia as the Engine of Application State. Responses include links to related actions/resources.
  5. Layered system: Client doesn't know if it's talking to the origin server or a proxy/cache.
  6. Code on demand (optional): Servers can send executable code (JavaScript) to clients.

What "stateless" really means: The server doesn't store session state BETWEEN requests. Each request is self-contained. A database is not "session state" — data stored in a DB is fine. Session cookies are NOT RESTful — they store session state on the server.

HATEOAS (rarely implemented): A truly RESTful response includes links. {"id": 1, "links": {"self": "/orders/1", "cancel": "/orders/1/cancel"}}. Lets clients navigate the API without out-of-band documentation. Almost no production API actually implements this.

The realization: Most APIs called "REST" or "RESTful" violate at least one constraint (usually HATEOAS). This is fine — REST is a guide, not a compliance standard.

Code Example

php
<?php
// STATELESS — each request carries its own authentication
// ❌ Stateful (non-RESTful): server stores session
session_start();
$_SESSION['user_id'] = 42; // server stores state!
// Next request: server looks up $_SESSION — stateful!

// ✅ Stateless: client sends token on every request
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Server validates token, extracts user_id from it — no stored session

// UNIFORM INTERFACE — resources identified by URLs, manipulated via HTTP methods
Route::get('/orders/{id}',    [OrderController::class, 'show']);    // GET = fetch representation
Route::put('/orders/{id}',    [OrderController::class, 'update']);  // PUT = replace representation
Route::delete('/orders/{id}', [OrderController::class, 'destroy']); // DELETE = remove

// CACHEABLE — server defines cacheability in headers
Route::get('/products/{id}', function (int $id) {
    $product = Product::findOrFail($id);
    return response()
        ->json($product)
        ->header('Cache-Control', 'public, max-age=300')
        ->header('ETag', md5($product->updated_at)); // for conditional requests
});

// HATEOAS — rarely implemented in practice, but this is what true REST looks like
class OrderResource extends \Illuminate\Http\Resources\Json\JsonResource
{
    public function toArray($request): array
    {
        return [
            'id'     => $this->id,
            'status' => $this->status,
            'total'  => $this->total,
            '_links' => [           // HATEOAS links!
                'self'    => ['href' => route('orders.show', $this->id)],
                'cancel'  => $this->status === 'pending'
                    ? ['href' => route('orders.cancel', $this->id), 'method' => 'DELETE']
                    : null,
                'invoice' => ['href' => route('orders.invoice', $this->id)],
            ],
        ];
    }
}

// LAYERED — client doesn't know about load balancers, caches, or CDNs
// Your API response headers make this transparent:
// Via: nginx/1.24.0
// X-Cache: HIT from cdn.example.com
// Age: 120

// The 6 constraints summary:
// 1. Client-Server: Separate concerns ✓ (SPA + API)
// 2. Stateless: JWT tokens, not sessions ✓
// 3. Cacheable: Cache-Control headers ✓
// 4. Uniform Interface: Proper HTTP methods + resource URLs ✓
// 5. Layered: Works through load balancers, CDNs ✓
// 6. Code on demand: (optional, rarely used in APIs)