REST API Introduction

REST principles, HTTP verbs mapped to CRUD, and a resource-oriented URL design example.

What REST actually means

REST (Representational State Transfer) is an architectural style for designing networked APIs, defined by Roy Fielding in his 2000 doctoral dissertation. It isn't a protocol or a standard you install — it's a set of constraints that, followed together, produce APIs that are predictable, cacheable, and easy to evolve. An API described as "RESTful" generally follows these core principles:

  • Resources — everything the API exposes is modeled as a resource (a user, an order, a product), each identified by a URL. You don't call a "verb" like /getUser; you address a "noun" like /users/42.
  • Uniform interface — a small, fixed set of operations (the standard HTTP methods) apply consistently across every resource, instead of each endpoint inventing its own action names.
  • Statelessness — every request from a client must contain all the information the server needs to process it. The server stores no client session state between requests; each request is handled independently, using data (like an auth token) the client sends every time.
  • Client-server separation — the client (UI) and server (data and business logic) evolve independently, connected only by the API contract between them.
  • Representations — the client never gets the resource itself; it gets a representation of its current state, almost always as JSON.

Resources, not actions

The biggest shift REST asks for, compared to older RPC-style ("Remote Procedure Call") APIs, is thinking in nouns instead of verbs. Instead of designing an endpoint per action:

Plaintext
POST /getUser?id=42
POST /createUser
POST /deleteUser?id=42
POST /updateUserEmail

REST models the resource (user) once, and lets the HTTP method express the action:

Plaintext
GET    /users/42        -> read a specific user
POST   /users           -> create a new user
PATCH  /users/42        -> partially update a user
DELETE /users/42         -> delete a user

This is the "uniform interface" constraint in practice: once you know a URL identifies a resource, you already know the full menu of things you can do to it, without reading endpoint-specific documentation for every single action.

HTTP verbs mapped to CRUD

HTTP method CRUD operation Typical use
GET Read Fetch a resource or a collection. Safe (no side effects) and cacheable.
POST Create Create a new resource inside a collection. Not idempotent — calling it twice creates two resources.
PUT Update (replace) Replace a resource entirely with the given representation. Idempotent — calling it twice has the same effect as calling it once.
PATCH Update (partial) Modify only the given fields of a resource.
DELETE Delete Remove a resource. Idempotent — deleting an already-deleted resource still results in it being gone.

"Idempotent" is a term worth being precise about: it means making the same request multiple times produces the same end state as making it once — it does not mean the response is identical every time (a repeated DELETE on an already-deleted resource might return 404 the second time, but the resource's state — "gone" — hasn't changed).

Resource-oriented URL design — a worked example

Consider designing an API for a blogging platform: posts, each with comments, written by authors.

Plaintext
GET    /authors/7/posts          -> list posts by author 7
GET    /posts/123                -> get a single post
POST   /posts                    -> create a new post
PATCH  /posts/123                -> update fields on post 123
DELETE /posts/123                -> delete post 123

GET    /posts/123/comments       -> list comments on post 123 (a nested resource)
POST   /posts/123/comments       -> add a comment to post 123
DELETE /posts/123/comments/45    -> delete comment 45 on post 123

Notice a few conventions that make this design predictable:

  • Plural nouns for collections (/posts, not /post).
  • Nesting expresses ownership/containment (/posts/123/comments — comments that belong to post 123), but avoid nesting more than two levels deep — /authors/7/posts/123/comments/45/replies/9 becomes painful to work with. Prefer /comments/45/replies once a resource has its own clear identity.
  • No verbs in the path. /posts/123/publish is a common, pragmatic exception for actions that don't map cleanly onto CRUD (see below) — but the default should always be a plain resource path plus an HTTP method.

When an action doesn't fit CRUD

Not everything is naturally create/read/update/delete. "Publish this draft post" or "reset this user's password" are actions, not field updates. Two common, accepted patterns:

Plaintext
POST /posts/123/publish        -> a sub-resource-like action endpoint (common, pragmatic)
PATCH /posts/123                -> { "status": "published" }   (modeling it as a field change)

Both are used in real-world APIs; the second is more strictly "RESTful," but the first is often clearer intent and is a widely accepted pragmatic compromise.

Common mistakes

  • Designing endpoints around actions (/createOrder, /cancelOrder) instead of resources (POST /orders, PATCH /orders/9 with a status change) — this is the most common way an API ends up "REST in name only."
  • Using GET for anything that changes server state — GET must always be safe (no side effects), since browsers, proxies, and crawlers may call it without the user's explicit intent (e.g., link prefetching).
  • Treating PUT and PATCH as interchangeable — PUT implies replacing the entire resource; PATCH implies a partial update. Sending a partial body to a PUT endpoint can unintentionally null out fields the client didn't include.

Interview questions

Q: What does it mean for an API to be "resource-oriented"? It means the API is modeled around nouns (resources like /users, /orders) identified by URLs, with a small, consistent set of HTTP methods (GET, POST, PUT, PATCH, DELETE) expressing actions on those resources — rather than one bespoke endpoint per action, as in RPC-style APIs.

Q: Why is statelessness one of REST's core constraints? Because it means any server instance can handle any request — there's no session state pinned to a specific server that a load balancer needs to route around. This makes RESTful APIs easier to scale horizontally and easier to reason about, since each request is self-contained and doesn't depend on server memory of prior requests.