⬆️ ⬇️

REST? Take a dumb JSON-RPC



Recently, a lot of controversy broke out on Habré about how to properly prepare the REST API.



Instead of raging in the comments, think: do you really need REST?

Is this a conscious choice or a habit?



Maybe your particular RPC-like API project will work better?

')





So what is JSON-RPC 2.0 ?

This is a simple stateless protocol for creating an RPC (Remote Procedure Call) API.

It usually looks like this.



You have one single endpoint on the server that accepts requests with the body of the form:



{"jsonrpc": "2.0", "method": "post.like", "params": {"post": "12345"}, "id": 1} 


And gives answers like:



 {"jsonrpc": "2.0", "result": {"likes": 123}, "id": 1} 


If an error occurs - an error response:



 {"jsonrpc": "2.0", "error": {"code": 666, "message": "Post not found"}, "id": "1"} 


And it's all!



The bonus supports batch operations:



 Request: [ {"jsonrpc":"2.0","method":"server.shutdown","params":{"server":"42"},"id":1}, {"jsonrpc":"2.0","method":"server.remove","params":{"server":"24"},"id":2} ] Response: [ {"jsonrpc":"2.0","result":{"status":"down"},"id":1} {"jsonrpc":"2.0","error":{"code":1234,"message":"Server not found"},"id": 2} ] 


In the id field, the API client can send anything, so that after receiving responses from the server, match them with requests.



Also, the client can send “notifications” - requests without the “id” field, which do not require a response from the server:



 {"jsonrpc":"2.0","method":"analytics:trackView","params":{"type": "post", "id":"123"}}, 




There are libraries for the client and the server, probably, for all popular languages.

If not, it doesn't matter. The protocol is so simple that it will take a couple of hours to write your implementation.



Working with the RPC client that I first got on npmjs.com looks like this:



 client.request('add', [1, 1], function(err, response) { if (err) throw err; console.log(response.result); // 2 }); 




Profits



Consistency with the business logic of the project



First, you can avoid hiding complex operations behind a meager set of HTTP verbs and redundant URIs.

There are subject areas where there must be more operations in the API than entities.

Offhand - projects with complex business processes, gamedev, instant messengers and similar realtime-things.



Yes, even take a content project like Habr ...

Pressing the "↑" button under the post is not a change in the resource, but a challenge to the whole chain of events, up to the issuance of icons or invites to the post author.

So is it worth masking post.like(id) for PUT /posts/{id}/likes ?



It is also worth mentioning CQRS , with which the RPC-shny API will look better.



Secondly, the response codes in HTTP are always smaller than the types of business logic errors that you would like to return to the client.

Someone always returns 200-ku, someone puzzles, trying to compare errors with HTTP-codes.

In JSON-RPC, the entire integer range is yours.



JSON-RPC - standard, not a set of recommendations



Very simple standard.

Request data can be:
RESTRPC
Request URI---
In GET Parameters---
HTTP headers---
In request bodyIn request body


Response data can be:
RESTRPC
In the HTTP response code---
HTTP headers---
In the body of the answer (the format is not standardized)In the response body (format is standardized)




POST /server/{id}/status or PATCH /server/{id} ?

It doesn't matter anymore. It remains to POST /api .



There are no best practices from the forums, there is a standard.

There is no disagreement in the team, there is a standard.



Of course, a well-implemented REST API can be fully documented. But…



Do you know what and where you need to pass in the request to the Github API to get the reactions object along with the issue?

Accept: application/vnd.github.squirrel-girl-preview
Is it good or bad? Decide for yourself, google yourself. There is no standard.



HTTP independence



In theory, REST principles can be applied not only to APIs over HTTP.

In practice, everything is different.



JSON-RPC over HTTP is safely transferred to JSON-RPC over Websocket. Yes, at least TCP.

The body of a JSON-RPC request can be directly queued in raw form in order to process it later.



No more problems from smearing business logic across the transport layer (HTTP).



HTTP 404
RESTRPC
There is no resource with this identifier.---
No API hereNo API here




Performance



JSON-RPC will come in handy if you have:

- Batch requests

- Notifications that can be processed asynchronously

- Web sockets



Not that this could not be done without JSON-RPC. But with him - a little easier.



Underwater rocks





HTTP caching



If you are going to cache your API responses at the HTTP level, RPC may not be suitable.

This usually happens if you have a public, mostly read-only API.

Something like getting a weather forecast or currency rate.



If your API is more "dynamic" and is intended for "internal" use - everything is ok.



access.log



All requests to the JSON-RPC API in the logs of the web server look the same.

It is solved by logging at the application level.



Documenting



There is no swagger.io level tool for JSON-RPC.

Apidocjs.com will do , but it is much more modest.

However, you can document this simple API even in a markdown file.



Stateless



“REST” is about architecture, but not HTTP verbs - you will object. And you will be right.



Roy Fielding’s original dissertation did not indicate which verbs, headings, and HTTP codes should be used.

But it has a magic word, which is useful even when designing an RPC API. "Stateless".



Each client request to the server must contain all the information necessary to fulfill this request, without storing any context on the server side. Session state is stored entirely on the client side.




By doing RPC API on top of web sockets, you may be tempted to force the application server to store a little more data about a client session than is necessary.

How stateless should an API be in order not to cause problems? For contrast, let's remember a truly statefull protocol - FTP.



: [ TCP-]

: 220 ProFTPD 1.3.1 Server (ProFTPD)

: USER anonymous

: 331 Anonymous login ok, send complete email address as your password

: PASS user@example.com

: 230 Anonymous access granted, restrictions apply

: CWD posts/latest

: 250 CWD command successful

: RETR rest_api.txt

: 150 Opening ASCII mode data connection for rest_api.txt (4321 bytes)

: 226 Transfer complete

: QUIT

: 221 Goodbye.





Session state is stored on the server. The FTP server remembers that the client has already been authenticated at the beginning of the session, and remembers the directory in which the client is currently located.



Such an API is difficult to develop, debug and scale. Do not do this.



Eventually



Take JSON-RPC 2.0 if you decide to make an RPC API over HTTP or web sockets.

You can, of course, invent your bike, but why?



Take GraphQL if you really need it.



Take gRPC or something like that for communication between (micro) services, if your YaP supports it.



Take REST if you need it. Now you at least choose it consciously.

Source: https://habr.com/ru/post/441854/



All Articles