RPC: When an API Models Operations Instead of Resources
- date
- category
- Backend
- also in
- Software Architecture · System Design
- reading
- 2 min / 372 words
REST starts from a resource:
POST /orders
PATCH /orders/42
RPC starts from an operation:
POST /createOrder
POST /approveOrder
POST /cancelOrder
RPC stands for Remote Procedure Call.
The mental model is simple:
local function call
|
v
network
|
v
remote execution
A procedure instead of a resource
Locally we could write:
approveInvoice(42);
RPC tries to keep similar semantics across the network boundary:
POST /rpc/approveInvoice
{
"invoiceId": 42
}
The backend maps the request onto a domain operation.
That often fits systems where commands matter more than CRUD.
RPC does not eliminate HTTP
RPC is an application model.
The transport can still be HTTP:
RPC method
|
v
HTTP request
|
v
TCP/QUIC
It is even possible to use a single endpoint:
POST /rpc
{
"method": "approveInvoice",
"params": {
"invoiceId": 42
}
}
That is how the JSON-RPC style works, among others.
The upside: domain semantics are explicit
Compare:
PATCH /payments/42
{
"status": "retried"
}
with:
POST /payments/42/retry
or with RPC:
retryPayment(42)
The second form says more about intent.
A status is not always input data. It is often the result of performing an operation.
RPC can model such cases better.
The price: part of the HTTP semantics is gone
If everything is:
POST /rpc
HTTP knows very little about the meaning of the operation.
It cannot easily tell:
- whether the operation is safe,
- whether it is idempotent,
- whether the response can be cached,
- whether a retry is safe.
The semantics move from the HTTP layer into the application contract.
That is why RPC needs a very good contract.
Typed RPC
Modern RPC systems often go further:
schema
|
v
code generation
|
v
typed client
|
v
typed server
gRPC uses Protocol Buffers for that.
Other tools generate the client straight from endpoint or type definitions.
That shortens the distance between:
client.approveInvoice({ id: 42 })
and an ordinary function call.
But the network remains the network:
- a timeout can happen,
- a request can arrive even when the response is lost,
- a retry can perform the operation again,
- the client and the server can hold different versions of the contract.
RPC simplifies the interface, but it does not remove the semantics of a distributed system.
The next limitation
REST and RPC share one property:
the backend defines the shape of the endpoint and of the response.
If different screens need different subsets and combinations of data, the number of endpoints or response variants starts to grow.
At that point the model can be inverted.
Instead of asking a particular endpoint for a predetermined payload, the client can state exactly which data it needs.
That is the entry point to GraphQL.