REST is nonsense
Few months ago, I was digging into a bug we had: basically, from time to time, we were marking orders as "paid", while Stripe capture failed.
The payment was done is two steps: the user gives its card information to Stripe, which captures the amount, and later on, when the order is validated, we call this endpoint to definitively capture the funds.
I looked at logs, we still get 200 errors, however, the payload contains
a field .status which should be set to "success", and obviously, it wasn't
checked.
The thing is, http responses come with a status code:
- 1xx informational response
- 2xx success
- 3xx redirection
- 4xx client errors
- 5xx server errors
The thing is, an error could not fit any of these, as they are business-related, not protocol-related.
But, most of the implementations are mixing the two, so we assumed it was the same thing too.
Let's focus on the requests for a moment.
Http comes with methods/verbs, they are all applied to a resource (the URI), the most common are:
GETfetch a representation of the resourcePOSTsends a payload to the resourcePUTreplace the resourcePATCHreplace some parts of the resourceDELETEremove the resource
Moreover, REST (which stands for Representational State Transfer) adds constraints.
Let's try to draft few endpoints:
- Create an order
POST /order
- See an order
GET /order/:orderId
- Add an item to an order
PUT /order/:orderIdPATCH /order/:orderIdPOST /order/:orderId/itemPOST /order/:orderId/item/:itemId
- Cancel an order
DELETE /order/:orderId
- Request an order to be refunded
POST /order/:orderId/refundDELETE /order/:orderId
REST add a structural constraint: everything should be a resource, bound by a state.
Every "complex" interactions such as business-related processes, or (potential) asynchronicity is, at best oddly handled.
Note: we could also mention UI/UX use cases, if you have a multi-users product, a lot of the interactions or business-related processes depending on other users' actions, which could happen at any time.
Alternatively, I would advocate to use things like json-rpc.
Requests have 4 attributes:
jsonrpc: a string with the protocol versionid: an integer used to identify the response (requests can be batched)method: the method invokedparams: an optional payload to perform the call
Response have 4 attributes:
jsonrpc: a string with the protocol versionid: an integer, matching the requestresult: an optional (on non-success) payloaderror: an optional (on success) payload
Let's rework our endpoints:
- Create an order
method: "create-order"
- See an order
method: "fetch-order", params: {orderId: 42}
- Add an item to an order
method: "add-order-item", params: {orderId: 42, itemId: 1024}
- Cancel an order
method: "cancel-order", params: {orderId: 42}
- Request an order to be refunded
method: "request-refund-order", params: {orderId: 42}
Here, we have gained a way to have a business-oriented API, focused on the processes and actions, not on states.
On another hand, to avoid dealing with asynchronicity/synchronicity fork, we could rely on Server-sent events in a web context.
It's a cheap way to have a push-based architecture (instead of a pull-based we have when we directly query the server).