LearnThatStack Ace your next interview

System Design Concepts · API Design

Thinking in resources: what REST actually is

REST is constraints, not JSON over HTTP: address resources, let methods decide the action, send hypermedia links to what's next and more.

CONSTRAINTSREST is a set of constraintsfive constraints make an api RESTFULRESTclient-servercacheableuniform interfacelayered system
CONSTRAINTSREST is a set of constraintsRESTclient-servercacheableuniform interfacelayered system

REST is a set of constraints

  1. REST starts as a name and nothing more. No constraints are in place yet, so the name has no meaning.
  2. The first two constraints are in place: client-server and stateless. REST starts to mean something.
  3. The next two constraints are in place: cacheable and uniform interface. Each added constraint gives the name REST more meaning.
  4. The fifth branch, the layered system constraint, joins the other four. All five constraints are met, so the REST label is fully drawn.
  5. A WebSocket design keeps session state on the server. So the branch for REST's stateless constraint is marked as broken.
  6. The stateless branch detaches from the tree of constraints and falls away. Statelessness is the one constraint being dropped.
  7. Four constraints are still in place, but stateless is gone. So the design is no longer REST.

© LearnThatStack - diagrams may not be republished without permission.

Ask an engineer what REST is and you usually get a tool list: JSON, HTTP verbs, a URL scheme. That list cannot tell a RESTful API apart from any other API that sends JSON.

REST is a set of constraints. First is client-server: the client owns the interface, the server owns the data. and Stateless means every request carries everything needed to answer it. So any instance behind the load balancer can take the next request.

Next is Cacheable. It means a response indicates whether a client or a proxy may keep a copy, so repeated reads never reach your server. Then, Uniform interface - it refers to the shape and structure: resources at addresses, standard methods, and links in the response.

Layered system is the fifth constraint. A client cannot tell whether it is talking to the origin server, a cache, or a load balancer in front of that server. This is REST - all these five constraints.

If a design broke one of the constraint say stateless. For example - a WebSocket connection holds session state on the server between messages, to know which client is connected, and what that client last saw. A request on that connection no longer carries everything needed to answer it, so its no longer stateless.

So you gave it up the stateless constraint to keep the connection open. The other four are still good, and the design can be the right one. WebSockets are the right call for a live chat feed.

Four constraint branches are still in place, and the API still sends JSON over HTTP. This is a WebSocket design, it is not REST.

What does 'the uniform interface' mean. The six POST endpoints on the left are each named after an action, so that API ignores the uniform interface. Every feature got its own name, which felt natural but it has its cost.

Instead, name the thing and you got one address, /users/123. The three user endpoints become methods on that address: GET reads the user, PUT replaces the user, DELETE removes the user. Six endpoints will become three, all on that one address. The method carries the action, and the URL is the name of the resource.

The collection for the item is the same noun, plural - /users. Finding a user by email is not an action worth a name. That search is a filter on the collection, GET /users?email=. The collection keeps one address, and you can add as many filters later.

Orders belong to a user, so /users/123/orders sits one level under /users/123 and allows GET and POST actions. The path defines who owns what. The left column, the list of actions, is empty now. Every action has an address and a method.

Be careful of nesting. The path /users/123/orders/456/items/7 repeats ids that already identify things on their own. Nesting is a habit that does not know where to stop. An order has its own id, so /orders/456 stands alone and items sit under that path. So usually, nest one level to show ownership, and stop there.

The address tree should read like the domain: users have orders. And a client should be able to guess addresses that match your entities. A verb in a path means you are calling a function over HTTP, not addressing a resource.

One constraint is still missing, and this is not commonly implemented in practice: the uniform interface also asks for links in the response. Two clients load the same order, status pending. Client A has a map of URLs and and got the Cancel and Pay buttons from that map. Client B got nothing, because nothing in the response said what Client B may do.

The server adds a _links block with cancel and pay, then sends the value to client B. Client B reads the links and got two things it can do next, Cancel and Pay. Sending the links with the value is HATEOAS, hypermedia as the engine of application state. The order's state decides which links appear, and those links decide what the client offers.

The order is paid, so the response says paid and the links say refund. The new value reaches Client B. Client B stops getting Cancel and Pay and got Refund instead, with no code change. Client A still got Cancel and Pay, because its map from state to buttons does not know the order is now paid.

Client A sends Cancel from its own map of URLs and gets a 409. Client B sends Refund from a link the server sent and gets a 200. B never built a URL and never offered an action the server had not offered first, so the server can move URLs and rules without breaking B.

Client A keeps working without the links, but client B has nothing left to display. So why does almost nobody ship hypermedia? Most APIs stop at level 2 of the Richardson model: resources and methods, URLs in the docs and the SDK, no links in the body. Level 3 adds the links.

A generated SDK fails the build when a URL moves. The build failure is the protection level 3 would have given at runtime. Most teams make that trade on purpose: an HTTP API at level 2. They instead use OpenAPI spec to publish their URL map.

REST is a set of constraints, and dropping one means you cannot call your API REST. In practice the uniform interface means addresses for things and methods for actions. Addresses nest one level deep to show ownership. Hypermedia is the links in response for what the client may do next, but many APIs skip this constraint.

In interviews · 4 questions

Related Questions

← All concepts