REST, GraphQL, and RPC
Three styles, three different things being optimised. None of them is a religion.
These are three ways of organising the same conversation. The differences come down to who decides what data is returned, and how many round trips a screen needs.
REST, organised by thing
- Endpoints are nouns: /users, /orders
- The server decides what each returns
- Very cacheable, very simple to inspect
- A rich screen may need several calls
GraphQL, organised by question
- One endpoint; the client describes what it wants
- One round trip can fill a whole screen
- No over-fetching of unused fields
- Caching and rate limiting get much harder
RPC is the third style and the most direct: instead of modelling nouns, you call a named function on the server as though it were local. This app is built that way: a server function is RPC with the network plumbing and type checking generated for you.
A reasonable default: RPC or server functions for your own frontend, REST for public APIs other people build against. Reach for GraphQL when you genuinely have many different clients wanting many different slices of the same data, which is a real problem but a less common one than the discourse suggests.
What to remember
- REST organises by resource, GraphQL by query, RPC by action.
- RPC with shared types is a strong fit for your own frontend.
- Public APIs benefit from REST’s predictability and cacheability.
Terms in this lesson
Field notes
Loaded from a deliberately slow source. The lesson above was already readable while this was still travelling. That is streaming, and it is the same trick a chat interface uses.
The URL that read everyone’s invoices
An invoice page checked that you were logged in and then loaded whatever id was in the address. Changing the number showed somebody else’s invoice. It was found by a customer who mistyped, not by a review.
Classic IDOR, still extremely common
The key in the client bundle
An API key was placed behind the public environment prefix so it would be readable from the frontend. It worked. It was also visible to every visitor, and was being used by strangers within a week.
Scraped from a public bundle
resolved in 901ms · region iad1
Hide field notes toggles a search param the loader reads. With it off, the slow promise is never created, so nothing streams.