The Replacement ■ Episode 06
In the year 2000, Roy Fielding submitted his doctoral dissertation at the University of California, Irvine. It described an architectural style called Representational State Transfer. It was thoughtful, well-reasoned, and rooted in the design principles of the early web. We read it, nodded sagely, and then spent the next quarter-century turning it into a religion.
Five verbs. Fifty-odd status codes. URL hierarchies deep enough to require a map. HATEOAS, which nobody implements but everyone pretends to understand. OpenAPI specifications longer than the application code they describe. All of this ceremony in service of a single, rather modest question: the client wants something from the server.
The Thirty-Line Specification
In 2005, the JSON-RPC specification appeared. It is thirty lines long. The entire protocol fits on a napkin, and not a large one. The idea is disarmingly simple: call a method, get a result.
One endpoint. One HTTP verb. One format.
POST /api
{
"method": "getUser",
"params": { "id": 42 },
"id": 1
}
The response:
{
"result": { "id": 42, "name": "Alice" },
"id": 1
}
No verb selection. No status code philosophy. No URL hierarchy to maintain, document, and argue about in code reviews. You send what you want. You receive what you asked for. The protocol is so thin you can forget it is there, which is precisely what a good protocol should aspire to.
A Study in Legibility
Consider a real-world request: you want the items from order number seven, but only the name and price fields, and you want the product details included.
REST:
GET /users/42/orders/7/items?include=product&fields=name,price
JSON-RPC:
{
"method": "getOrderItems",
"params": {
"orderId": 7,
"fields": ["name", "price"]
}
}
The first is a puzzle. You read it left to right, parsing
path segments like a compiler, mentally separating resource
identifiers from query parameters, wondering whether
include is a standard feature or a custom
invention. The second is a sentence. A method name that
says what it does, parameters that say what they contain.
A junior developer can read it on their first day. A
senior developer can debug it without consulting the API
documentation.
Where REST Earns Its Keep
REST is not without merit, and intellectual honesty demands we say so. Public APIs benefit from REST conventions because every developer already knows them. Browser caching works beautifully with GET requests and proper cache headers. File downloads map naturally to resource URLs. Legacy systems that already speak REST do not need rewriting for the sake of architectural purity. If your API is consumed by unknown third parties who expect standard HTTP semantics, REST is the path of least friction.
These are genuine strengths, not consolation prizes.
Where JSON-RPC Wins
Internal service communication. Complex operations that do not map cleanly to CRUD verbs. Mobile and SPA backends where you control both ends of the wire. Batch requests, which JSON-RPC handles natively by sending an array of calls. Any situation where the question is not "which resource?" but "what operation?"
The moment your API verbs start looking tortured, you know
the model is wrong. POST /users/42/password-resets
is not a resource. It is a verb pretending to be a noun
because the framework demands it. {"method":
"resetPassword", "params": {"userId": 42}} says
what it means without the costume.
The Specification Gap
The JSON-RPC specification is thirty lines. The OpenAPI specification for a medium-sized REST API is three thousand. That ratio is not an exaggeration. It is arithmetic.
Three thousand lines of YAML describing URL patterns, query parameter types, response schemas for every status code, authentication flows, pagination conventions, and error formats. All of it machine-readable, none of it machine-necessary. The server already knows what it accepts. The client already knows what it sends. The specification exists to bridge the gap between the two, but the gap only exists because REST created it.
JSON-RPC closes the gap by not opening it. The method name
is the documentation. The parameters are the schema. The
response is either a result or an error. You do not need
three thousand lines of YAML to explain what
getUser does.
REST started as a dissertation. We turned it into a religion. JSON-RPC is thirty lines, one verb, and a method name. Sometimes the simplest protocol is the one that gets out of the way.