REST APIs: What They Are and How They Work
Every time your phone loads your email, a weather app fetches today's forecast, or a payment form charges your card — an API is doing the work behind the scenes. And the overwhelming majority of those APIs are built on REST.
You've probably heard the term. You might even have used REST APIs as a client. But what is REST, really? Not the surface-level definition, but the actual idea — why it was designed the way it was, what constraints it imposes, and why those constraints produce something useful.
That's what this article is about. We're not writing code yet. We're building the mental model first — because every design decision you'll make in later articles flows from understanding REST deeply, not just knowing the jargon.
By the end, you'll be ready to start designing your first REST API from scratch. Let's go.
Quick Reference
What REST is: An architectural style for distributed systems, defined by 6 constraints, that uses HTTP as its transport.
What REST is not: A protocol, a standard, a library, or a specification. There is no REST rulebook to download.
Core idea: Clients and servers communicate through a uniform interface, exchanging representations of resources over stateless requests.
Key terms:
- Resource — A thing your API exposes (a user, a task, an order)
- Representation — A snapshot of a resource at a point in time (usually JSON)
- Endpoint — A URL that identifies a resource
- Verb — An HTTP method (GET, POST, PUT, DELETE) that describes the action
Gotchas:
- ⚠️ REST is not the same as HTTP — HTTP is the transport REST typically uses
- ⚠️ "RESTful" is a spectrum, not a binary — most production APIs are partially RESTful
- ⚠️ REST was defined in a dissertation in 2000 — not by a committee, not by a company
See also:
- Resource Design: URLs, Nouns, and Hierarchies (coming soon) — the next article in this module
- HTTP Methods and Status Codes: The Full Picture (coming soon)
Version Information
Relevant specifications:
- HTTP/1.1: RFC 7230–7235
- HTTP/2: RFC 7540
- REST dissertation (Fielding, 2000): Architectural Styles and the Design of Network-based Software Architectures
Note: REST is transport-agnostic in theory — it predates HTTP/2 and works equally well over it. All examples in this module use HTTP/1.1 conventions for clarity, but everything applies to HTTP/2.
Last verified: June 2025
What You Need to Know First
This is the first article in the REST API Design & Specification module — no prior API knowledge is required.
You should be comfortable with:
- Basic web concepts: What a browser is, what a URL looks like, roughly what happens when you visit a website
- JSON syntax: What
{ "key": "value" }looks like and means (we'll use it throughout)
That's genuinely it. Everything else gets introduced as we go.
What We'll Cover in This Article
By the end of this guide, you'll understand:
- What REST actually is — its origin, its definition, and what makes it different from other API styles
- The 6 architectural constraints that define REST and why each one exists
- How HTTP maps onto REST — verbs, status codes, and the anatomy of a request and response
- What REST is not — and what "RESTful" really means in practice
- How REST compares to SOAP and GraphQL
- The TaskFlow scenario — the API we'll design together across this entire module
What We'll Explain Along the Way
Don't worry if you're unfamiliar with these — we'll define them as we encounter them:
- HTTP methods (GET, POST, PUT, DELETE, PATCH)
- HTTP status codes (200, 201, 404, 500, etc.)
- Client-server architecture
- Statelessness
- Caching
- JSON as a data format
The Problem REST Was Designed to Solve
Let's start with a story.
It's 1999. The web is growing fast — not just in page count, but in ambition. Companies are starting to realize their software needs to talk to other software. Businesses want their inventory systems to sync with their shipping partners. Developers want to build apps that pull data from other services.
The dominant solution at the time was SOAP (Simple Object Access Protocol) — a messaging protocol that wrapped everything in XML and enforced a strict contract between client and server. It worked, but it was heavy. You needed to negotiate a formal contract (called a WSDL — Web Services Description Language) before any communication could happen. The complexity was real.
A computer scientist named Roy Fielding was finishing his PhD dissertation at UC Irvine. His topic: what made the World Wide Web itself so scalable? The web had grown from thousands to billions of pages without anyone redesigning its architecture. Why?
Fielding identified the architectural constraints that made the web work — and named the resulting style REST: Representational State Transfer. His 2000 dissertation defined it formally. Within a decade, it had become the dominant way to design web APIs.
The insight at the core of REST is elegant: if you design your API the same way the web works, you get the same properties the web has — scalability, simplicity, and independence between the pieces.
The 6 REST Constraints
REST isn't a format or a protocol. It's a set of 6 architectural constraints, as defined in Fielding's dissertation, Chapter 5. A system that satisfies all 6 is RESTful. Each constraint exists for a reason — let's discover them one by one.
1. Client-Server
The constraint: The client and server are separate. The client handles the user interface. The server handles data storage and business logic. Neither knows the internal details of the other.
Why it matters: Separation of concerns. Your iOS app, your Android app, and your web app can all use the same API because the API doesn't know or care what kind of client is talking to it. The server can evolve its database without touching the client. The client can redesign its UI without touching the server.
[Client: iOS App] → [HTTP Request] → [Server: TaskFlow API]
[Client: iOS App] ← [HTTP Response] ← [Server: TaskFlow API]
2. Stateless
The constraint: Each request from a client to a server must contain all the information needed to understand and process that request. The server stores no session state between requests.
Why it matters: This is the constraint most developers find surprising at first. Let's dig in.
Imagine a server that remembers who you are between requests. Every time you send a message, it thinks: "Ah, this user is logged in, they've been browsing tasks." Now scale that to a million users. The server is holding a million sessions in memory. When it crashes and restarts, all those sessions are gone. When you add a second server, it doesn't have those sessions, so you need complex session synchronization.
Statelessness eliminates this entire problem. Every request carries everything the server needs — including authentication. The server processes it and forgets it. This makes servers:
- Easier to scale — any server can handle any request
- Easier to recover — a restart loses nothing
- Easier to reason about — each request is self-contained
The tradeoff: clients carry more responsibility. Your app needs to send your auth token with every request, not just once at login.
3. Cacheable
The constraint: Responses must label themselves as cacheable or non-cacheable. When a response is cacheable, clients (and intermediaries like CDNs — Content Delivery Networks) are allowed to reuse it for equivalent future requests.
Why it matters: Caching is one of the most powerful performance tools available. If your API returns the same list of countries in every response for a year, there is no reason for every client to hit your server separately. A cached response can be served instantly, without touching the network.
HTTP already has a rich caching system — Cache-Control, ETag, Last-Modified headers. REST plugs into this system naturally. A well-designed REST API gets caching nearly for free.
4. Uniform Interface
The constraint: All interactions happen through a standardized interface, regardless of who the client is or what the server does internally.
This is the central constraint that defines REST's character. It has four sub-constraints:
- Resource identification: Resources are identified by URIs — Uniform Resource Identifiers (e.g.,
/tasks/42) - Resource manipulation through representations: Clients interact with resources by sending representations (e.g., JSON) — they don't manipulate the resource directly
- Self-descriptive messages: Each message includes enough information to describe how to process it (content type, verbs, status codes)
- Hypermedia as the engine of application state (HATEOAS): Responses include links to related actions — a concept we'll explore in the advanced article of this module
Why it matters: Uniformity means any client can talk to any server without prior arrangement, as long as both follow the same interface conventions. The interface is HTTP — the most widely understood protocol in existence.
5. Layered System
The constraint: The client doesn't need to know whether it's talking directly to the server or to an intermediary (a load balancer, a cache, an API gateway, a security proxy).
Why it matters: This enables the entire infrastructure layer that sits between clients and servers — CDNs, API gateways, authentication proxies, rate limiters. None of that infrastructure requires clients to know it exists. You can add, remove, or change layers without touching a single client.
6. Code on Demand (Optional)
The constraint: Servers can extend client functionality by sending executable code (JavaScript, for example).
This is the only optional constraint in REST — and it's rarely relevant to API design in the traditional sense. Websites use it constantly (every <script> tag is code on demand), but JSON APIs almost never do.
We mention it for completeness. You can safely set it aside for the rest of this module.
How HTTP Maps onto REST
REST is transport-agnostic in theory. In practice, it runs on HTTP almost universally. Here's why: HTTP already implements most of REST's constraints. It has a uniform interface (verbs and headers), a caching system, a status code vocabulary, and a stateless request-response model. REST and HTTP are a natural fit.
Let's look at the anatomy of an actual HTTP interaction.
A Request
Every HTTP request has the same structure:
POST /tasks HTTP/1.1
Host: api.taskflow.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...
{
"title": "Review pull request",
"assignee": "user_42",
"due_date": "2025-07-01"
}
Breaking this down:
POST— The HTTP method (or verb). Tells the server what kind of action you want to perform./tasks— The path. Identifies the resource you're acting on.HTTP/1.1— The protocol version.Host— Which server to send this to.Content-Type: application/json— "The body I'm sending is JSON."Authorization: Bearer ...— "Here's my proof of identity." (Statelessness in action — sent with every request.)- The body — The representation of the resource being created.
A Response
HTTP/1.1 201 Created
Content-Type: application/json
Location: /tasks/99
{
"id": "task_99",
"title": "Review pull request",
"assignee": "user_42",
"due_date": "2025-07-01",
"status": "open",
"created_at": "2025-06-15T10:30:00Z"
}
Breaking this down:
201 Created— The status code. "Your request succeeded and created a new resource."Content-Type: application/json— "My response body is JSON."Location: /tasks/99— "The new resource lives here."- The body — The representation of the newly created task.
HTTP Methods: The Verb Vocabulary
HTTP gives us a set of verbs, each with a defined meaning. REST uses these to represent the four fundamental operations on data — often called CRUD (Create, Read, Update, Delete). The semantics of each method are formally defined in RFC 7231:
| HTTP Method | CRUD Operation | What It Means | Safe? | Idempotent? |
|---|---|---|---|---|
GET | Read | Retrieve a resource | ✅ | ✅ |
POST | Create | Create a new resource | ❌ | ❌ |
PUT | Update | Replace a resource entirely | ❌ | ✅ |
PATCH | Update | Modify part of a resource | ❌ | ❌ |
DELETE | Delete | Remove a resource | ❌ | ✅ |
Two terms in that table deserve explanation:
Safe means the request doesn't change server state. GET is safe — reading a task doesn't modify it. POST is not safe — it creates something new.
Idempotent means calling the request multiple times produces the same result as calling it once. DELETE /tasks/99 removes task 99. Calling it again still results in task 99 being gone (the server returns 404, but the state is the same). POST /tasks is not idempotent — calling it three times creates three tasks.
We'll cover all HTTP methods in full detail in HTTP Methods and Status Codes: The Full Picture (coming soon).
HTTP Status Codes: The Response Vocabulary
Status codes tell the client what happened. They're grouped into five families, as documented in MDN's HTTP response status codes reference:
| Range | Family | Meaning | Examples |
|---|---|---|---|
| 1xx | Informational | Request received, processing | 100 Continue |
| 2xx | Success | Request succeeded | 200 OK, 201 Created |
| 3xx | Redirection | Further action needed | 301 Moved Permanently |
| 4xx | Client Error | The client made a mistake | 400 Bad Request, 404 Not Found |
| 5xx | Server Error | The server made a mistake | 500 Internal Server Error |
The pattern is consistent: 2xx means things worked, 4xx means the client did something wrong (fix the request before retrying), 5xx means the server broke (the client can safely retry).
What REST Is Not
Before we go further, let's clear up three persistent misconceptions.
REST is not a protocol
A protocol defines an exact format and sequence of messages — like SMTP (Simple Mail Transfer Protocol, used for email) or FTP (File Transfer Protocol). REST defines architectural constraints, not message formats. Two REST APIs can look quite different from each other and both be perfectly valid.
REST is not the same as HTTP
HTTP is the most common transport for REST APIs, but REST doesn't require HTTP. You could theoretically implement REST over a completely different protocol. Conversely, you can use HTTP for things that aren't REST at all — like SOAP, which runs over HTTP but violates several REST constraints.
"RESTful" is a spectrum
Roy Fielding noted in a 2008 blog post that most APIs calling themselves "REST APIs" don't actually satisfy the uniform interface constraint — particularly the HATEOAS sub-constraint. He was technically correct.
In practice, the industry uses "REST API" to mean an HTTP API that uses URLs as resource identifiers and HTTP verbs to represent operations. This is what we'll design in this module. It satisfies the most practically valuable REST constraints and is what every developer means when they say "REST API."
REST vs SOAP vs GraphQL
You'll encounter comparisons between these three approaches. Here's a summary drawing on the GraphQL documentation and MDN's web API overview:
| REST | SOAP | GraphQL | |
|---|---|---|---|
| Style | Architectural constraints | Protocol with strict spec | Query language |
| Format | Usually JSON | Always XML | Always JSON |
| Contract | Informal convention | Formal WSDL contract | Schema |
| Flexibility | High | Low | Very high |
| Caching | Native HTTP caching | Complex | Difficult |
| Learning curve | Low | High | Medium |
| Best for | General-purpose web APIs | Enterprise integration, financial systems | Complex data graphs, mobile clients needing precision |
When REST wins: You're building a public API, a mobile backend, or a service consumed by a wide variety of clients. The low barrier to entry and native HTTP tooling make REST the pragmatic default.
When GraphQL wins: Your clients have very different data needs (a mobile app wants minimal fields, a dashboard wants everything). GraphQL lets clients specify exactly what they need.
When SOAP wins: You're integrating with enterprise systems that require formal contracts, message-level security, or built-in retry semantics. Banking and healthcare integrations still use it heavily.
For the rest of this module, we're building REST.
Introducing TaskFlow: Our Running Scenario
Every article in this module will use the same API as its working example: TaskFlow, a task management API.
Here's the scenario: you've been hired to design and build the backend API for a new task management product. The product team has given you these requirements:
- Users can create, view, update, and delete tasks
- Tasks can be assigned to other users
- Tasks belong to projects
- Users can comment on tasks
- The API will be consumed by a web app, an iOS app, and an Android app simultaneously
Over the course of this module, you'll design TaskFlow's resources, its URL structure, its request and response shapes, its versioning strategy, its OpenAPI specification, its authentication scheme, and finally audit it against a checklist of common API design mistakes.
By the end, you'll have a complete, production-quality API design — with clear reasoning behind every decision.
For now, here are the top-level resources TaskFlow will expose:
/users — People who use TaskFlow
/projects — Groups of related tasks
/tasks — Individual units of work
/comments — Notes attached to tasks
Simple enough to be understandable. Complex enough to surface every interesting design challenge.
How a REST Interaction Works: End to End
Let's trace through a complete TaskFlow interaction to see everything working together.
A user opens the TaskFlow mobile app and loads their task list.
Diagram: A client request travels through an API gateway (which validates the auth token) to the API server, which queries the database. The response travels back through the same chain. The gateway adds a cache header, so the next identical request within 60 seconds can be served without hitting the server.
What's happening in REST terms:
- Client-server: The iOS app and the TaskFlow API are separate — the app knows nothing about how the server stores data
- Stateless: The bearer token in the header is everything the server needs to identify the user — no session stored anywhere
- Layered: The app doesn't know the API gateway exists — it just sends a request to
api.taskflow.com - Cacheable: The
Cache-Control: max-age=60header tells intermediaries they can cache this response for 60 seconds - Uniform interface: Standard HTTP verb (
GET), standard URL (/tasks), standard status code (200), standard format (JSON)
Five of the six REST constraints, visible in a single interaction.
Common Misconceptions
❌ Misconception: REST requires JSON
Reality: REST has no opinion about data format. A REST API can return XML, YAML, plain text, HTML, or binary data. JSON became the dominant format because it's lightweight and maps naturally to JavaScript — not because REST requires it.
Why this matters: You'll encounter REST APIs that return XML (especially in older enterprise systems). They're not "doing REST wrong" — they're using a different representation format. The Content-Type header tells the client what format to expect.
❌ Misconception: A REST API just means using HTTP
Reality: Using HTTP doesn't make an API RESTful. Many APIs use HTTP as a transport but violate REST constraints — for example, by using POST for every operation (including reads), or by encoding the action in the URL (/getTasks, /createTask).
Why this matters: Understanding what makes an API actually RESTful helps you make better design decisions — not just follow surface-level conventions without understanding why.
❌ Misconception: Stateless means the application has no state
Reality: Stateless refers to request state, not application state. Your TaskFlow database stores plenty of state — tasks, users, comments. What stateless means is that the server doesn't store session state about the client between requests. Database state is fine. Session state is what must live on the client.
Example:
❌ What stateful session handling looks like:
Request 1: POST /login → Server stores "user_42 is logged in" in memory
Request 2: GET /tasks → Server looks up "who is making this?" from memory
✅ What stateless handling looks like:
Request 1: POST /login → Server returns a token to the client
Request 2: GET /tasks + Authorization: Bearer <token> → Server reads identity from the token
❌ Misconception: REST is always the right choice
Reality: REST is an excellent default for general-purpose web APIs. But if your clients need highly flexible data fetching (GraphQL's strength), or you need formal message-level contracts (SOAP's strength), REST may not be the best fit. The right architecture depends on your specific constraints — not on what's most popular.
Troubleshooting Common Issues
Problem: "I don't know if my API is RESTful enough"
Symptoms: Uncertainty about whether design decisions are correct; conflicting advice online about what REST "requires."
Reality: There is no certification for REST compliance. Practically speaking, if your API uses URLs to identify resources, HTTP verbs to represent operations, and returns standard status codes — it's RESTful enough for production use.
Diagnostic approach: Ask these three questions about each endpoint:
- Does the URL identify a thing (a noun), not an action (a verb)?
- Does the HTTP method accurately describe what the operation does?
- Does the status code accurately describe what happened?
If yes to all three, you're in good shape.
Problem: "I'm not sure when to use REST vs GraphQL"
The decision framework:
- Do all your clients need roughly the same data? → REST is simpler
- Do different clients need very different slices of the same data? → GraphQL may be worth the complexity
- Is your team small and the timeline tight? → REST has a lower learning curve and more tooling
- Is performance on slow mobile connections critical? → GraphQL's precise fetching may be worth it
Problem: "My team keeps debating whether something is 'really REST'"
Resolution: Agree on a practical definition as a team and move on. Suggest this one: "We use URLs to identify resources, HTTP verbs to represent operations, and standard status codes to describe outcomes." Everything beyond that is a design choice, not a REST requirement.
Check Your Understanding
Quick Quiz
-
What are the six REST architectural constraints?
Show Answer
Client-Server, Stateless, Cacheable, Uniform Interface, Layered System, and Code on Demand (optional). The first five are the practically important ones.
-
What's wrong with this API design?
GET /getTasksByUser?userId=42
POST /createTask
POST /deleteTask?id=99Show Answer
Several problems:
- URLs contain verbs (
getTasksByUser,createTask,deleteTask) — REST uses nouns for resource identification DELETEis being done withPOSTinstead of theDELETEHTTP method — the uniform interface isn't being used
The correct REST design:
GET /tasks?assignee=42
POST /tasks
DELETE /tasks/99 - URLs contain verbs (
-
A client sends
GET /tasks/99. The task doesn't exist. What status code should the server return?Show Answer
404 Not Found. The client asked for a resource that doesn't exist. This is a client error (4xx) because the client requested a non-existent resource — the server is working correctly. -
True or False: Stateless means the TaskFlow database cannot store any data between requests.
Show Answer
False. Stateless refers to session state — the server doesn't remember which client made which previous requests. The database stores application data (tasks, users, etc.) as normal. Stateless means each HTTP request carries all the information needed to process it (including auth), rather than relying on the server having remembered a previous interaction.
Hands-On Exercise
Challenge: Identify every REST violation in the following API design, then rewrite it correctly.
POST /api/doLogin
POST /api/fetchAllTasks
POST /api/markTaskComplete?id=55
POST /api/removeTask?id=55
GET /api/getUserInfo?user=42
Show Answer
Violations identified:
POSTis used for reads (fetchAllTasks,getUserInfo) —GETshould be used for read operations- URLs contain verbs (
doLogin,fetchAllTasks,markTaskComplete,removeTask) — REST uses nouns POSTis used for deletion and state changes that have specific HTTP methods (DELETE,PATCH)
Corrected design:
POST /sessions — Login (creates a session token)
GET /tasks — Fetch all tasks
PATCH /tasks/55 — Update task (mark complete via body: { "status": "complete" })
DELETE /tasks/55 — Remove task
GET /users/42 — Get user info
Each URL now identifies a resource (noun), each HTTP method describes the operation accurately, and the uniform interface is used as intended.
Summary: Key Takeaways
-
REST is an architectural style, not a protocol or a standard. It was defined by Roy Fielding in his 2000 dissertation by observing what made the web itself scalable.
-
Six constraints define REST: Client-Server (separation of concerns), Stateless (each request is self-contained), Cacheable (responses label themselves), Uniform Interface (HTTP verbs + URLs), Layered System (intermediaries are invisible), and Code on Demand (optional).
-
HTTP maps naturally onto REST because it already implements most REST constraints. HTTP verbs (GET, POST, PUT, PATCH, DELETE) represent operations. URLs identify resources. Status codes (2xx, 4xx, 5xx) describe outcomes.
-
Stateless doesn't mean stateless applications. Your database stores state normally. Stateless means the server doesn't hold session state between requests — each request carries its own authentication.
-
REST is a pragmatic choice, not a dogmatic one. Most production APIs satisfy the most useful REST constraints. Perfect HATEOAS compliance is rare and often unnecessary.
-
TaskFlow — the task management API we'll design across this module — will give every concept in subsequent articles a concrete, consistent home.
What's Next?
You now understand what REST is, why it exists, and how HTTP implements its constraints.
The natural next step is Resource Design: URLs, Nouns, and Hierarchies (coming soon) — because the most consequential decision in any REST API design is how you model your resources and structure your URLs. Everything downstream (request design, versioning, documentation) depends on getting this right.
Once you've worked through resource design, HTTP Methods and Status Codes: The Full Picture (coming soon) will complete the foundational vocabulary you need to start making real design decisions.
References
- Architectural Styles and the Design of Network-based Software Architectures — Roy Fielding's original REST dissertation, UC Irvine, 2000. Primary source for the 6 REST constraints and the origin story of REST.
- REST APIs Must Be Hypertext-Driven — Roy Fielding's 2008 clarification on what truly constitutes a REST API, used for the "RESTful is a spectrum" discussion.
- HTTP/1.1 Semantics and Content (RFC 7231) — IETF specification formally defining HTTP methods and their semantics (safe, idempotent).
- HTTP response status codes — MDN Web Docs reference for all HTTP status code families and meanings.
- An overview of HTTP — MDN Web Docs, used for the HTTP request/response anatomy section.
- GraphQL Introduction — Official GraphQL documentation, used for the REST vs GraphQL comparison.