A REST API is an API that follows the REST architectural style: resources at URLs, stateless requests, and standard HTTP methods. REST — Representational State Transfer, defined in Roy Fielding’s 2000 dissertation — is a style, not a protocol or standard: a set of constraints that, honored together, produce APIs the whole web already knows how to consume, cache, and scale.
Key takeaways
| Question | Answer |
|---|---|
| The model | Resources at URLs · representations (usually JSON) · standard methods |
| The source | Fielding’s 2000 dissertation, chapter 5 — an architectural style, not a spec |
| The six constraints | Client-server · stateless · cacheable · uniform interface · layered · code on demand (optional) |
| The verbs | GET · POST · PUT · PATCH · DELETE — with safety and idempotency semantics |
| The honest footnote | Most production “REST” APIs are level-2 HTTP APIs — and that’s fine |
A full CRUD cycle in raw HTTP
The whole style in four requests — this is what every framework and SDK ultimately sends:
POST /v1/posts → 201 Created create
{ "title": "Hello REST" } Location: /v1/posts/8fk2
GET /v1/posts/8fk2 → 200 OK read
{ "title": "Hello REST", … }
PUT /v1/posts/8fk2 → 200 OK replace
{ "title": "Hello again" } (PATCH would update fields)
DELETE /v1/posts/8fk2 → 204 No Content delete
GET /v1/posts/8fk2 → 404 Not Found …and it's gone
The same cycle through SDKs that wrap the REST calls:
// JavaScript / Node.js — Back4app JS SDK
// The REST semantics, wrapped: create, read, update, delete
const post = new Parse.Object('Post');
post.set('title', 'Hello REST');
await post.save(); // POST /classes/Post → 201
const fetched = await new Parse.Query('Post')
.get(post.id); // GET /classes/Post/:id → 200
fetched.set('title', 'Hello again');
await fetched.save(); // PUT /classes/Post/:id → 200
await fetched.destroy(); // DELETE /classes/Post/:id → 200 // Flutter / Dart — Back4app Flutter SDK
// The REST semantics, wrapped: create, read, update, delete
final post = ParseObject('Post')..set('title', 'Hello REST');
await post.save(); // POST /classes/Post → 201
await post.fetch(); // GET /classes/Post/:id → 200
post.set('title', 'Hello again');
await post.save(); // PUT /classes/Post/:id → 200
await post.delete(); // DELETE /classes/Post/:id → 200 // iOS / Swift — Back4app Swift SDK
// The REST semantics, wrapped: create, read, update, delete
var post = Post()
post.title = "Hello REST"
let saved = try await post.save() // POST /classes/Post → 201
let fetched = try await saved.fetch() // GET /classes/Post/:id → 200
var updated = fetched
updated.title = "Hello again"
_ = try await updated.save() // PUT /classes/Post/:id → 200
try await updated.delete() // DELETE /classes/Post/:id → 200 // Android / Kotlin — Back4app Android SDK
// The REST semantics, wrapped: create, read, update, delete
val post = ParseObject("Post")
post.put("title", "Hello REST")
post.save() // POST /classes/Post → 201
val fetched = ParseQuery.getQuery<ParseObject>("Post")
.get(post.objectId) // GET /classes/Post/:id → 200
fetched.put("title", "Hello again")
fetched.save() // PUT /classes/Post/:id → 200
fetched.delete() // DELETE /classes/Post/:id → 200 The six constraints of REST
- Client-server — interface and implementation evolve independently; the UI never knows how storage works.
- Stateless — every request is self-contained; the server holds no session between calls, which is what lets any replica answer any request.
- Cacheable — responses declare their own cacheability; GETs with proper cache headers make the web’s entire caching infrastructure (browsers, CDNs, proxies) work for your API.
- Uniform interface — the constraint that is REST, in four parts: resources identified by URIs; manipulation through representations (you send back the JSON you want the resource to become); self-descriptive messages (method + headers say everything needed to process the request); and hypermedia as the engine of application state (responses link to next actions).
- Layered system — clients can’t tell whether they’re talking to the origin, a cache, or a gateway; intermediaries slot in freely.
- Code on demand (optional) — servers may ship executable code to clients; the one constraint marked optional, and the one most APIs ignore.
HTTP methods: safety, idempotency, CRUD
The table missing from nearly every ranking page — RFC 9110’s semantics, condensed:
| Method | CRUD role | Safe? | Idempotent? | Retry blindly? |
|---|---|---|---|---|
| GET | Read | Yes | Yes | Yes |
| POST | Create | No | No | No — may duplicate |
| PUT | Replace | No | Yes | Yes — same result |
| PATCH | Partial update | No | Not guaranteed | Depends on patch design |
| DELETE | Remove | No | Yes | Yes — still gone |
Safe means the request changes nothing; idempotent means repeating it changes nothing further. These aren’t trivia — they are the retry policy: a network timeout on PUT can be retried without fear, the same timeout on POST needs an idempotency key or a duplicate check. Full CRUD mapping has its own entry.
Status codes: what to return when
| Situation | Return |
|---|---|
| Read succeeded | 200 OK |
| Created a resource | 201 Created + Location header |
| Deleted; nothing to say | 204 No Content |
| Malformed request | 400 Bad Request |
| No/invalid credentials | 401 Unauthorized |
| Authenticated but not allowed | 403 Forbidden |
| No such resource | 404 Not Found |
| Over the rate limit | 429 Too Many Requests (details) |
| Server fault | 500 Internal Server Error |
The 401/403 and 200/201/204 distinctions are where API craftsmanship shows: precise codes make clients debuggable with nothing but the status line.
Is your API really REST? The maturity ladder
The honest section commercial explainers omit. The Richardson Maturity Model grades HTTP APIs: level 0 (one URL, one verb, RPC in disguise), level 1 (resources at URLs), level 2 (proper methods and status codes), level 3 (hypermedia — HATEOAS).
By Fielding’s own insistence, an API without hypermedia isn’t REST — he wrote a pointed essay saying exactly that. In practice, almost every acclaimed “REST API” is a level-2 HTTP API: resources, verbs, status codes, JSON, no hypermedia. This matters less as purity and more as vocabulary — knowing the ladder tells you what the term means in a job posting (level 2) versus the dissertation (level 3), and saves you from both cargo-cult HATEOAS and pedantic corrections.
REST vs. SOAP vs. GraphQL vs. gRPC
| REST | SOAP | GraphQL | gRPC | |
|---|---|---|---|---|
| Nature | Architectural style | Protocol | Query language + runtime | RPC framework |
| Wire | JSON over HTTP | XML envelopes | JSON over HTTP (one endpoint) | Protobuf over HTTP/2 |
| Contract | OpenAPI (convention) | WSDL (mandatory) | Schema (built-in) | .proto (mandatory) |
| Caching | HTTP-native — its superpower | Poor | Application-level | Application-level |
| Best at | Public resource CRUD | Enterprise/legacy formality | Client-shaped nested data | Internal service speed |
| Weakness | Fixed shapes over/underfetch | Verbosity | Cache & rate-limit complexity | Browser friction |
The GraphQL comparison gets a full entry of its own.
Conventions that make a REST API pleasant
Beyond the constraints, the conventions consumers silently grade you on: plural-noun resources (/posts, not /getPost); nesting one level max (/posts/8fk2/comments, then stop); pagination on every collection — cursor-based for depth and stability, with limits enforced; filtering and sorting as query parameters, not endpoint variants; versioning with an explicit policy (/v1/ path or header — pick one, publish deprecation windows); content negotiation honored (Accept, Content-Type); and errors as structured JSON with a machine-readable code, not just prose. None of this is in the dissertation; all of it is in the difference between an API developers recommend and one they endure.
Common use cases
- Public and partner APIs — REST’s ubiquity is the feature: every language, tool, and developer speaks it.
- Mobile and web app backends — resource CRUD over HTTP matches how most app screens actually consume data.
- Microservice seams — internal contracts where HTTP’s tooling (gateways, tracing, caching) earns its keep.
- Webhook-style integrations — systems notifying systems with plain HTTP calls both sides already understand.
- Auto-generated data APIs — platforms that expose a database as REST resources — the fastest route from schema to working API.
Should you use REST? A decision matrix
| REST is the right default when… | Reach for something else when… |
|---|---|
| Public API, unknown consumers | Internal high-throughput mesh → gRPC |
| Resource-shaped CRUD domain | Clients need to shape nested responses → GraphQL |
| HTTP caching can carry read load | Real-time bidirectional push → WebSockets / live queries |
| Simplicity and tooling breadth matter | Formal enterprise contracts required → SOAP |
| Screens map cleanly to resources | One screen aggregates five services → composite endpoint / BFF |
Limitations and trade-offs
- Fixed representations misfit diverse clients. The over/underfetching pair is REST’s structural weakness; sparse fieldsets and expansion parameters mitigate, GraphQL redesigns.
- No mandatory contract. Nothing forces an OpenAPI spec, so many REST APIs are documented by folklore; discipline is opt-in where gRPC and GraphQL make it structural.
- Statelessness repeats context. Auth and tenant context ride every request — cheap in bytes, but it pushes session semantics to tokens and makes some flows (multi-step transactions) awkward.
- N+1 by design temptation. Resource-per-URL thinking invites one-call-per-item clients; good APIs ship expansion and batch affordances before consumers improvise loops.
- “REST” the word is ambiguous. Level-2 HTTP API in most mouths, hypermedia architecture in the dissertation — read which one a spec, job posting, or reviewer means before arguing.
REST APIs on Back4app
Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. The REST API here is generated, not built: every class in your data model is immediately a resource — POST /classes/Post creates, GET /classes/Post/:id reads, with the methods, status codes, and Location semantics from the walkthrough above — behind keys, user tokens, and class-level permissions enforcing the boundary. The code tabs show the same cycle through the SDKs, which are thin idiomatic wrappers over exactly this HTTP; when an operation outgrows CRUD, a Cloud Code function adds a custom endpoint in one file. Level-2 REST, correct by default, from schema to URL in the time it takes to define the class.
Frequently asked questions
What is a REST API in simple terms?
A way for two applications to talk over HTTP using conventions everyone already knows: each thing (a user, an order) lives at a URL, you act on it with a standard verb — GET to read, POST to create, PUT or PATCH to update, DELETE to remove — and every request stands alone, carrying everything the server needs to answer it.
What does REST stand for?
Representational State Transfer, from Roy Fielding's 2000 doctoral dissertation. The name describes the mechanism: the server transfers a representation of a resource's state (usually JSON) to the client, and the client moves the application from state to state through those representations.
What is the difference between REST and RESTful?
In everyday usage, nothing — the terms are interchangeable. Pedantically, REST names the architectural style and RESTful is the adjective for an API implementing it. The circulating claim that "RESTful follows all the rules and REST only some" has no basis in Fielding's work.
What are the six REST constraints?
Client-server separation, statelessness, cacheability, uniform interface, layered system, and — optionally — code on demand. The uniform interface itself unpacks into four rules: resources identified by URIs, manipulation through representations, self-descriptive messages, and hypermedia as the engine of application state.
What is the difference between PUT and POST?
Idempotency and addressing. POST creates under a collection — the server assigns the URL, and repeating the request creates duplicates. PUT writes a full representation to a known URL — repeating it yields the same state, which makes retries safe. That safety difference, not style, is why the distinction matters.
Does a REST API have to use JSON?
No. REST is format-agnostic — a resource can be represented as JSON, XML, HTML, or an image, negotiated through the Accept and Content-Type headers. JSON is simply the modern default because every client parses it cheaply. The constraint is about representations, not about any particular one.
What does stateless mean in a REST API?
The server keeps no memory of the client between requests: each request carries everything needed to process it, including credentials such as a bearer token. The payoff is horizontal scale — any server can answer any request — and the cost is a few repeated bytes of context per call.
What is HATEOAS?
Hypermedia As The Engine Of Application State: responses include links to the actions available next, so clients navigate the API the way people navigate the web — by following links rather than hardcoding URLs. It is the least-implemented constraint; most production "REST" APIs skip it and live happily at level 2 of the maturity model.