REST Properly Understood

Fundamentals Beginner 18 min Jan 12, 2026

Audience

This guide is for developers who want to understand what REST really means:

  • Backend developers building APIs who want to make informed architectural decisions
  • Frontend developers consuming APIs who want to understand why APIs behave certain ways
  • API designers who need to explain REST constraints to their teams
  • Anyone confused about what makes an API “RESTful” (and tired of hearing contradictory definitions)

You should be familiar with basic HTTP concepts. If not, start with HTTP for REST APIs.

Goal

After reading this guide, you’ll understand:

  • What REST actually is (an architectural style, not a protocol)
  • The six constraints Roy Fielding defined and why each matters
  • The difference between resources and representations
  • Why statelessness is non-negotiable and what it enables
  • What HATEOAS means and why almost nobody implements it
  • How to assess whether an API is truly RESTful
  • Why “REST API” is often a misnomer (and why that might be okay)

This guide builds criteria—the ability to evaluate REST compliance and make conscious trade-offs.

1. The REST Misconception

Let’s start with an uncomfortable truth: most “REST APIs” aren’t REST.

When developers say “REST API,” they usually mean:

  • Uses HTTP
  • Returns JSON
  • Has URLs like /users/123
  • Uses GET, POST, PUT, DELETE

That’s not REST. That’s “HTTP with JSON.”

REST (Representational State Transfer) is an architectural style defined by Roy Fielding in his 2000 doctoral dissertation. It’s a set of constraints that, when applied together, create systems with specific properties: scalability, simplicity, portability, and evolvability.

graph TB
    subgraph "What People Think REST Is"
        A[HTTP + JSON] --> B[CRUD Endpoints]
        B --> C[/users, /products]
    end

    subgraph "What REST Actually Is"
        D[Architectural Style] --> E[6 Constraints]
        E --> F[Emergent Properties]
        F --> G[Scalability]
        F --> H[Evolvability]
        F --> I[Simplicity]
    end

    style A fill:#ffccbc
    style D fill:#c8e6c9

Why does this matter? Because understanding the real REST helps you:

  • Make better API design decisions
  • Know when to follow REST and when to consciously break it
  • Communicate precisely with your team
  • Stop cargo-culting patterns without understanding why

2. Where REST Came From

Roy Fielding didn’t invent REST in a vacuum. He was one of the principal authors of HTTP/1.1 and co-founded the Apache HTTP Server project. REST emerged from his experience building the web.

The Web as the Ultimate Distributed System

The World Wide Web is arguably the most successful distributed system ever created:

  • Billions of users
  • Millions of servers
  • Decades of evolution without breaking backward compatibility
  • Built on open standards anyone can implement

Fielding asked: What architectural principles make the web work?

His dissertation was an analysis of what made the web successful. REST is the architectural style he extracted from that analysis.

REST Is a Set of Constraints

Here’s the key insight: REST isn’t a specification you implement. It’s a set of constraints you apply to your architecture. Each constraint provides specific benefits, and applying them together creates emergent properties.

Think of it like building construction:

  • A “foundation” isn’t a specific product—it’s a constraint that buildings need stable bases
  • Different buildings implement foundations differently (concrete slab, pier and beam, basement)
  • The constraint guides design without dictating implementation

REST works the same way. It tells you what properties your system should have, not how to implement them.

3. The Six REST Constraints

Fielding defined six constraints. Let’s examine each one and what happens when you violate it.

Constraint 1: Client-Server

The constraint: Separate user interface concerns from data storage concerns.

graph LR
    subgraph "Client"
        A[User Interface]
        B[User Experience]
    end

    subgraph "Server"
        C[Data Storage]
        D[Business Logic]
    end

    A <-->|HTTP| C

    style A fill:#e3f2fd
    style C fill:#fff3e0

Why it matters:

  • Clients and servers can evolve independently
  • You can have multiple clients (web, mobile, CLI) for one server
  • Server can scale without affecting client code
  • Teams can work in parallel

What breaks when violated: If your “API” requires specific client behavior or embeds client-specific logic, you lose portability. Every new client needs special handling.

Constraint 2: Stateless

The constraint: Each request must contain all information necessary to understand and process it. The server stores no client context between requests.

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over S: Server knows nothing about Client

    C->>S: GET /cart (with auth token)
    Note over S: Reads token, fetches cart, forgets client
    S->>C: Cart contents

    C->>S: POST /cart/items (with auth token + item data)
    Note over S: Reads token, adds item, forgets client
    S->>C: Updated cart

    Note over S: Still knows nothing about Client

Why it matters:

  • Any server can handle any request (horizontal scaling)
  • No session affinity required (load balancer freedom)
  • Requests can be cached
  • Failures don’t corrupt server state
  • Easier debugging (each request is self-contained)

What breaks when violated:

If the server stores session state:

  • You need sticky sessions (client always goes to same server)
  • Server failure loses user state
  • Scaling requires session replication
  • Caching becomes difficult

Common misconception: “Stateless means no user data.”

Wrong. Stateless means no client session state on the server between requests. The server can store resource state (user profiles, orders, etc.). It just can’t store application state (shopping cart in memory, wizard progress, etc.) between requests.

The client sends everything needed:

GET /cart HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

The token contains or references all context the server needs.

Constraint 3: Cacheable

The constraint: Responses must explicitly define themselves as cacheable or non-cacheable.

Why it matters:

  • Reduces server load (cached responses don’t hit origin)
  • Reduces latency (client or proxy serves from cache)
  • Enables CDNs and edge caching
  • Scales read-heavy workloads dramatically

What breaks when violated:

If cacheability isn’t explicit:

  • Intermediaries can’t optimize
  • Clients make unnecessary requests
  • You lose the biggest scaling lever on the web
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
ETag: "abc123"

{
  "user": {"id": 123, "name": "Alice"}
}

The Cache-Control header tells everyone: “Cache this for 1 hour.” Without it, caching behavior is undefined.

Constraint 4: Uniform Interface

The constraint: All components communicate through a standardized interface.

This is the most fundamental constraint. It has four sub-constraints:

4.1 Identification of Resources

Every resource has a unique identifier (URI).

/users/123          ← User 123
/orders/456         ← Order 456
/users/123/orders   ← Orders belonging to user 123

A resource is any concept that can be named. Users, orders, documents, searches—anything.

4.2 Manipulation Through Representations

Clients don’t directly modify resources. They send representations of resources.

graph LR
    subgraph "Resource (Abstract)"
        R[User 123]
    end

    subgraph "Representations (Concrete)"
        J[JSON]
        X[XML]
        H[HTML]
    end

    R --> J
    R --> X
    R --> H

    style R fill:#fff3e0
    style J fill:#e3f2fd
    style X fill:#e3f2fd
    style H fill:#e3f2fd

The same resource (User 123) can have multiple representations:

  • JSON for APIs
  • XML for legacy systems
  • HTML for browsers

The client sends a representation to modify the resource:

PUT /users/123 HTTP/1.1
Content-Type: application/json

{"name": "Alice Smith", "email": "[email protected]"}

The server updates the resource based on this representation.

4.3 Self-Descriptive Messages

Each message contains enough information to understand how to process it.

GET /users/123 HTTP/1.1
Accept: application/json

---

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=3600

{"id": 123, "name": "Alice"}

The Content-Type tells you how to parse the body. The Cache-Control tells you how to cache it. No out-of-band knowledge required.

4.4 Hypermedia as the Engine of Application State (HATEOAS)

This is where REST gets controversial.

The constraint: Clients should navigate the application entirely through hyperlinks provided by the server.

GET /users/123 HTTP/1.1

---

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 123,
  "name": "Alice",
  "_links": {
    "self": {"href": "/users/123"},
    "orders": {"href": "/users/123/orders"},
    "edit": {"href": "/users/123", "method": "PUT"},
    "delete": {"href": "/users/123", "method": "DELETE"}
  }
}

The response tells the client: “Here’s what you can do next.”

Why HATEOAS matters:

  • Clients don’t need hardcoded URLs
  • Server can change URLs without breaking clients
  • Discoverability: new features appear as new links
  • Server controls navigation flow

Why almost nobody implements it:

  • Adds payload overhead
  • Requires sophisticated client logic
  • Most APIs have documentation instead
  • Mobile apps prefer static endpoints for performance

We’ll discuss this trade-off later.

Constraint 5: Layered System

The constraint: The architecture can be composed of hierarchical layers, each only knowing about the layer it directly interacts with.

graph TB
    C[Client] --> LB[Load Balancer]
    LB --> CDN[CDN / Cache]
    CDN --> GW[API Gateway]
    GW --> S1[Service A]
    GW --> S2[Service B]

    style C fill:#e3f2fd
    style LB fill:#fff3e0
    style CDN fill:#fff3e0
    style GW fill:#fff3e0
    style S1 fill:#c8e6c9
    style S2 fill:#c8e6c9

Why it matters:

  • Add caching layers without changing clients or servers
  • Add security layers (API gateway, WAF) transparently
  • Scale components independently
  • Replace components without affecting others

What breaks when violated:

If clients need to know about internal architecture (which server to hit, how services communicate), you’ve coupled everything together. Changes ripple through the system.

Constraint 6: Code on Demand (Optional)

The constraint: Servers can extend client functionality by transferring executable code.

This is the only optional constraint. Examples:

  • JavaScript in web browsers
  • Java applets (remember those?)
  • WebAssembly modules

Most REST APIs don’t use this. It’s mainly relevant for browsers.

4. Resources vs. Representations

This distinction is fundamental to REST and widely misunderstood.

Resources Are Abstract

A resource is any concept that can be named and identified. It’s abstract—you can’t touch it.

Examples of resources:

  • A specific user (User 123)
  • The current weather in Madrid
  • A document version at a point in time
  • A search result for “REST APIs”
  • The author of this guide

A resource has identity (URI), but it’s not the data itself.

Representations Are Concrete

A representation is concrete data that represents the resource’s current state.

The same resource can have many representations:

# JSON representation
GET /users/123 HTTP/1.1
Accept: application/json

{"id": 123, "name": "Alice", "email": "[email protected]"}

# XML representation
GET /users/123 HTTP/1.1
Accept: application/xml

<user><id>123</id><name>Alice</name><email>[email protected]</email></user>

# HTML representation (for browsers)
GET /users/123 HTTP/1.1
Accept: text/html

<html><body><h1>Alice</h1><p>[email protected]</p></body></html>

Same resource, different representations.

Why This Matters

Understanding resource vs. representation clarifies API design:

Wrong thinking: “My API returns user objects.”

Right thinking: “My API exposes user resources. Clients request representations in their preferred format.”

This enables:

  • Content negotiation (client requests preferred format)
  • Multiple clients (JSON for mobile, HTML for web, XML for legacy)
  • Versioned representations (same resource, different schema versions)
  • Partial representations (fields parameter, sparse fieldsets)

5. The REST Maturity Model

Leonard Richardson proposed a maturity model to classify APIs:

graph TB
    L0[Level 0: The Swamp of POX]
    L1[Level 1: Resources]
    L2[Level 2: HTTP Verbs]
    L3[Level 3: Hypermedia Controls]

    L0 --> L1
    L1 --> L2
    L2 --> L3

    style L0 fill:#ffccbc
    style L1 fill:#fff9c4
    style L2 fill:#c8e6c9
    style L3 fill:#b3e5fc

Level 0: The Swamp of POX

One URI, one HTTP method (usually POST). The HTTP protocol is just a tunnel.

POST /api HTTP/1.1

{"action": "getUser", "userId": 123}

This is SOAP, XML-RPC, or “RPC over HTTP.” Not REST at all.

Level 1: Resources

Multiple URIs, one method. Resources are identified, but HTTP semantics are ignored.

POST /users/123 HTTP/1.1

{"action": "get"}

POST /users/123 HTTP/1.1

{"action": "delete"}

You have resources, but everything is POST.

Level 2: HTTP Verbs

Multiple URIs, appropriate HTTP methods. This is where most “REST APIs” live.

GET /users/123
POST /users
PUT /users/123
DELETE /users/123

HTTP methods have semantic meaning. Status codes are used correctly. This is “HTTP done right.”

Level 3: Hypermedia Controls (HATEOAS)

Responses include links to related actions. The API is self-describing.

{
  "id": 123,
  "name": "Alice",
  "_links": {
    "self": "/users/123",
    "orders": "/users/123/orders",
    "deactivate": "/users/123/deactivate"
  }
}

This is full REST.

What Level Should You Target?

Level 2 is the pragmatic sweet spot for most APIs:

  • Uses HTTP correctly
  • Good developer experience
  • Mature tooling support
  • Well-understood patterns

Level 3 is ideal but costly:

  • Requires sophisticated clients
  • Increases payload size
  • Limited tooling support
  • Most benefits require client adoption

Most successful public APIs (Stripe, Twilio, GitHub) are Level 2. They’re not “pure REST,” but they’re well-designed HTTP APIs.

6. Why Most APIs Aren’t REST (And That’s Okay)

Here’s a liberating truth: you don’t have to build a REST API.

REST is one architectural style among many. It optimizes for:

  • Long-lived systems
  • Many independent clients
  • Evolvability over time
  • Web-scale distribution

If your system doesn’t need these properties, REST constraints may be overhead.

When REST Makes Sense

  • Public APIs consumed by unknown third parties
  • Systems that must evolve without breaking clients
  • Large-scale distributed systems
  • APIs that benefit from caching

When REST Might Not Be Ideal

  • Internal microservices (gRPC often better)
  • Real-time applications (WebSockets)
  • Complex queries (GraphQL)
  • Simple scripts or tools (just make it work)

The Honest Approach

Instead of saying “We have a REST API” when you don’t, say:

  • “We have an HTTP API following REST-like conventions”
  • “We have a JSON API with resource-oriented URLs”
  • “We follow Level 2 of the Richardson Maturity Model”

Precision helps your team make better decisions.

7. Common Misconceptions

Let’s address frequent misunderstandings:

“REST means CRUD”

No. REST is about resources and representations. CRUD (Create, Read, Update, Delete) is one way to think about resource operations, but REST doesn’t require it.

Resources can support operations that don’t map to CRUD:

  • POST /orders/123/cancel
  • POST /documents/456/publish
  • POST /users/789/verify-email

These are valid in REST if the operation affects resource state.

“REST means no versioning”

No. Fielding argues that properly RESTful systems shouldn’t need versioning because HATEOAS enables evolution. But in practice, most APIs need versioning because:

  • They’re Level 2, not Level 3
  • Breaking changes happen
  • Clients don’t always update

Versioning is a pragmatic necessity for most real-world APIs.

“REST means JSON”

No. REST doesn’t specify a data format. JSON is popular because:

  • Lightweight and human-readable
  • Native to JavaScript
  • Wide tooling support

But XML, HTML, or Protocol Buffers are equally valid for REST. The key is self-description (Content-Type header) and content negotiation (Accept header).

“REST is the only way to build APIs”

Definitely no. REST is one tool in your toolkit. Consider:

  • GraphQL for complex queries with varying data needs
  • gRPC for high-performance internal services
  • WebSockets for real-time bidirectional communication
  • Server-Sent Events for server push

Choose based on your requirements, not dogma.

8. Practical Takeaways

Here’s how to apply this knowledge:

When Designing APIs

  1. Be honest about your constraints. Are you building full REST or “HTTP with JSON”? Both are valid.

  2. Prioritize Level 2. Use HTTP methods correctly, return proper status codes, make resources identifiable. This gets you 80% of the benefit.

  3. Consider HATEOAS selectively. Adding links to related resources helps discoverability without full HATEOAS commitment.

  4. Keep statelessness. This is the most valuable constraint. Never store session state on the server.

  5. Enable caching. Add Cache-Control headers. This scales your read traffic.

When Evaluating APIs

Ask these questions:

  • Are resources clearly identified by URIs?
  • Do HTTP methods match their semantics?
  • Are responses self-describing (Content-Type, status codes)?
  • Is the API stateless?
  • Can responses be cached?

If yes to all, you have a well-designed HTTP API—even if it’s not “full REST.”

When Discussing REST

Use precise language:

  • “RESTful” → API following all REST constraints including HATEOAS
  • “REST-like” or “RESTish” → API using REST conventions (Level 2)
  • “HTTP API” → API using HTTP correctly without REST claims

This prevents endless debates about what is or isn’t REST.


What’s Next

This guide covered REST as an architectural style—what it really means, why it exists, and how to evaluate it.

For deeper topics like:

  • Modeling complex resources and relationships
  • When to consciously break REST constraints
  • Refactoring poorly designed APIs
  • Advanced HATEOAS patterns

See the upcoming course: Professional REST Design: Criteria and Trade-offs.


Deepen your understanding:

  • REST - The architectural style in depth
  • Resource - The core abstraction of REST
  • Stateless - Why and how to eliminate server-side session state
  • RESTful API - What makes an API truly RESTful
  • Client-Server - The fundamental separation pattern
  • HTTP - The protocol that implements REST on the web