Why we chose JSON-RPC over REST

If you don’t want to mess about with XML, REST is pretty much the industry standard for creating an API.

Initially, we had a REST(ish) API. But after using it internally at HomeRez, we were not very happy with how it works. So we looked around for alternatives.

In the end, we decided to go for JSON-RPC.

What are the differences?

  • REST uses HTTP  or HTTPS . JSON-RPC can use any transport protocol, for example TCP .
  • JSON-RPC has 1 end-point URL for all requests. REST uses different URLs for different resources.
  • In JSON-RPC, any request is sent the same way (e.g. via HTTP POST ) with the method and parameters in it. In REST, you use the HTTP verbs ( GET , POST , PUT , DELETE ) for different actions.

So, what made us switch from REST to JSON-RPC?

The main reason is that we couldn’t find a good way to map all operations in our API to HTTP verbs. We have several operations that are not pure Create, Read, Update or Delete operations.

For example, we want a call to calculate the rent for staying at an apartment for a specific period. That isn’t really  GET /apartment/rent  because we’re not retrieving a “rent” object that we can then PUT  to update. It also isn’t something like POST /apartment/calculate_rent , because that isn’t very RESTish.

Cancelling a reservation is another operation that gave us doubts. Calling PUT /reservation/<id>  with data { guestFirstName: "John" }  seems very different compared to calling it with data { status: "CANCELLED" } . The first simply updates a field, while the second has a lot of side effects: emails being sent to the guest and the owner, payments being refunded, the apartment becoming available again, etc. Maybe POST /reservation/<id>/cancel  would be ok, but that also doesn’t seem very RESTish – after all, you are modifying a reservation.

It became clear to us that we wanted to have an action-based API, where most of the calls perform actions. Many of those actions are different from from the traditional CRUD operations.

One other thing that bothered us, is GET  requests with lots of parameters. For example, let’s say you want to search reservations by a guest named “John Doe”. In JSON, the search parameters could look something like this:

However, if you put this information in the GET parameters, it becomes a bit tricky. You need to take escaping into account (where &  becomes &amp; ). If you just do the traditional ?a=1&b=2  parameters, you don’t have support for sub-structures. So you could turn your parameters into a JSON string, encode it, and then decode it on the server, but why make it so complex?

Yes, for a URL that you visit in your browser, it’s great that everything is in the address bar. It’s great that you can bookmark such a search. But we’re talking about an API here, not about a page being visited in the browser.

So, now we post all API calls to the same URL, with a method and a parameter object. Authentication fields are also sent in the parameter object, so we can easily switch our transport layer from HTTPS  to something else for better performance, if we want.

An additional advantage is that we can now easily use json-schema both to validate the incoming requests and to auto-generate most of our API documentation.

Examples of our calls are:

  • reservation.create  to create a reservation
  • reservation.quote  to get a quote (rent calculation) for a specific vacation home and period
  • reservation.cancel  to cancel a reservation
  • reservation.list  to get a list of reservations based on the search parameters
  • property.list  to get a list of properties (vacation homes) based on the search parameters
  • property.rate.list  to get all rates of a single vacation home

All in all, we are very happy with this switch.

This entry was posted in software development. Bookmark the permalink.

7 Responses to Why we chose JSON-RPC over REST

  1. Andreas Heck says:

    Initially I was a huge fan of REST. But the basic problem of REST is that it is a principle to design a system for retrieving hyperlinked documents. In the beginning that feels just very familiar because we use the web everyday and one might think “Yeah, why shouldn’t APIs work the same way?”. The problem is when you have an API you ALWAYS want to call functions at some point. Even if you have a REST API, in your program you just want to call a function that gets something done and not manipulate a resource by using one of four verbs.

    So you put a lot of time and effort into modeling your API in a way that fits the constraints of REST although defining equivalent functions is something you could do in a fraction of the time. And when you are done with that you think about how to remap this REST API to function calls that you will actually use in your code. JSON-RPC is probably the most straight forward solution one could think of.

    As someone who currently implements his own self-describing JSON-RPC library I would love to know more about how you use JSON Schema in your solution and how you generate documentation from it.

    • Joost says:

      You seem to have gone through the same thought process as we have :-)

      Your question about how we use JSON Schema has a pretty long and complex answer. I hope I can do it justice in this comment box.

      Each JSON-RPC call is defined in its own file. This file contains an object with the method name, the function that handles the calls to this method, a summary, a description, the JSON-schema for the request object, and the JSON-schema for the response object.

      We use these definition files both to create the JSON-RPC server, and to generate documentation HTML files that are served via express.

      For the server, we use https://www.npmjs.com/package/multitransport-jsonrpc. However, instead of putting functions directly into the server constructor, we use a wrapper.

      This wrapper does several things. First, it makes sure that the request for each call is validated (with the “tv4” module) against the correct JSON Schema, and also the response. Second, it gives each request a unique id, and gives the corresponding response the same id, so it’s easier to find matching request and response in log files. Third, it converts Errors to JSON-RPC error message. Fourth, it takes care of authentication via a callback that is passed during creation of the server. Fifth… well, it does more but you get the point.

      Hopefully this answers your question a bit!

  2. Andreas Heck says:

    Thanks for your comprehensive answer. It gave me a good understanding of your concept. Never used json-schema so far but your approach is quite interesting.

    Need to give json-schema a try in the future I guess.

  3. Chris Veness says:

    It seems to me this reflects lack of understanding of REST rather than failings of REST. I would recommend the O’Reilly book “RESTful Web Services” by Richardson & Ruby: I think that would answer your issue of how to “find a good way to map all operations in our API to HTTP verbs”.

    Incidentally, REST works very well with XML (as well as with JSON).

    For your examples, RESTful equivalents might be

    POST /reservations to create a reservation
    GET /reservations/quote?apartment=12&week=21 to get a quote
    DELETE /reservations/123456 to cancel a reservation
    GET /reservations?apartment=12 to get a list of reservations
    GET /properties to get a list of properties
    GET /properties/1234 to get all details (incl rates) of a single property

    or for rates if you didn’t want all details, you could create a new (sub-)resource

    GET /properties/1234/rates to get rates of a single property

    If you check RESTful Web Services you will find there are many philosophical and practical benefits to a RESTful approach over an RPC interface.

    • Joost says:

      Thank you for your comprehensive reply, Chris. You appear to know much more about REST than I, and I have added your recommendation to my reading list.

      Since I wrote this blog post, I have changed jobs, and at my new job REST was already being used. The extra tools that we can use there, like Swagger, certainly outweigh any (real or imaginary) disadvantage that REST has compared to JSON-RPC.

      One question though. If you had to design this reservation API in REST, would you approach both the following operations with a PUT request? What is the reasoning behind your answer?

      1. Update the reservation’s guest’s first name from “J” to “John” (no side effects)
      2. Update the reservation’s status from PENDING to CONFIRMED (side effects: sends emails to guest and host, starts payment procedure, updates internal dashboard, possibly causes a bell to be tolled in the office because it’s the 1000th reservation)

      • Chris Veness says:

        Good questions.

        1) is easy, though I would probably use PATCH in preference to PUT (strictly speaking, a PUT resets an entire resource, so any unspecified fields should be deleted or reset to null).

        For 2), I think there is general consensus that side-effects are acceptable in REST interfaces (with certain constraints), either on related resources or external actions. Conveniently, PATCH is not defined to be idempotent, so it seems fine to me for the state transfer to trigger e-mails and other side-effects (RFC 5789: “The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources”).

        Of course, there will be decisions about what actions and status responses to make if side-effects fail (e.g. if payment succeeds but e-mail fails) – this would also be true of an RPC interface. A 207 Multi-Status response might be appropriate, or you could atomicise the operations with successive PATCH calls to set status=in-process, payment=processed (triggering payment side-effect), email=sent (triggering email confirmation), status=confirmed; in which case the client would be in charge of individual steps and any recovery actions.

  4. Leandro Cofre says:

    Thanks for this article!

    Just one question, How do you document your API?

Leave a Reply

Your email address will not be published.