{"id":441,"date":"2016-01-03T17:29:50","date_gmt":"2016-01-03T16:29:50","guid":{"rendered":"http:\/\/joost.vunderink.net\/blog\/?p=441"},"modified":"2016-01-03T22:24:55","modified_gmt":"2016-01-03T21:24:55","slug":"why-we-chose-json-rpc-over-rest","status":"publish","type":"post","link":"https:\/\/joost.vunderink.net\/blog\/2016\/01\/03\/why-we-chose-json-rpc-over-rest\/","title":{"rendered":"Why we chose JSON-RPC over REST"},"content":{"rendered":"<p>If you don&#8217;t want to mess about with XML, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Representational_state_transfer\">REST<\/a> is pretty much the industry standard for creating an API.<\/p>\n<p>Initially, we had a REST(ish) API. But after using it internally at HomeRez, we were not very happy with how it works. So\u00a0we looked around for alternatives.<\/p>\n<p>In the end, we\u00a0decided to go for <a href=\"http:\/\/www.jsonrpc.org\/specification\">JSON-RPC<\/a>.<\/p>\n<p>What are the differences?<\/p>\n<ul>\n<li>REST uses <span class=\"lang:default decode:true crayon-inline \">HTTP<\/span>\u00a0 or <span class=\"lang:default decode:true crayon-inline \">HTTPS<\/span>\u00a0. JSON-RPC can use any transport protocol, for example <span class=\"lang:default decode:true crayon-inline \">TCP<\/span>\u00a0.<\/li>\n<li>JSON-RPC has 1 end-point URL for all requests. REST uses different URLs for different resources.<\/li>\n<li>In JSON-RPC, any request is sent the same way (e.g. via <span class=\"lang:default decode:true crayon-inline \">HTTP POST<\/span>\u00a0) with the method and parameters in it. In REST, you use the HTTP verbs (<span class=\"lang:default decode:true crayon-inline \">GET<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline \">POST<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline \">PUT<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline \">DELETE<\/span>\u00a0) for different actions.<\/li>\n<\/ul>\n<p>So, what made us switch from REST to JSON-RPC?<\/p>\n<p>The main reason is that we <strong>couldn&#8217;t find a good way to map all operations in our API to HTTP verbs<\/strong>.\u00a0We have several operations that are not pure Create, Read, Update or Delete operations.<\/p>\n<p>For example, we want a call to calculate the rent for staying at an apartment for a specific period. That isn&#8217;t really\u00a0<span class=\"lang:default decode:true crayon-inline\">GET \/apartment\/rent<\/span>\u00a0\u00a0because we&#8217;re not retrieving a\u00a0&#8220;rent&#8221;\u00a0object that we can then <span class=\"lang:default decode:true crayon-inline \">PUT<\/span>\u00a0 to update. It also isn&#8217;t\u00a0something like <span class=\"lang:default decode:true crayon-inline \">POST \/apartment\/calculate_rent<\/span>\u00a0, because\u00a0that isn&#8217;t very RESTish.<\/p>\n<p>Cancelling a reservation is another operation that gave us doubts. Calling <span class=\"lang:default decode:true crayon-inline \">PUT \/reservation\/&lt;id&gt;<\/span>\u00a0 with data <span class=\"lang:default decode:true crayon-inline \">{ guestFirstName: &#8220;John&#8221; }<\/span>\u00a0\u00a0seems very different compared to calling it with data <span class=\"lang:default decode:true crayon-inline \">{ status: &#8220;CANCELLED&#8221; }<\/span>\u00a0. 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 <span class=\"lang:default decode:true crayon-inline \">POST \/reservation\/&lt;id&gt;\/cancel<\/span>\u00a0 would be ok, but that also doesn&#8217;t seem very RESTish &#8211; after all, you are modifying a reservation.<\/p>\n<p>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.<\/p>\n<p>One other thing that bothered us, is <span class=\"lang:default decode:true crayon-inline \">GET<\/span>\u00a0 requests with lots of parameters.\u00a0For example, let&#8217;s say you want to search reservations by a guest named &#8220;John Doe&#8221;. In JSON, the search parameters could look something like this:<\/p>\n<pre class=\"lang:default decode:true \" title=\"Guest arguments for a search reservation call\">{\r\n    \"guest\": {\r\n        \"firstName\": \"John\",\r\n        \"lastName\": \"Doe\"\r\n    }\r\n}\r\n<\/pre>\n<p>However, if you put this information in the GET parameters, it becomes a bit tricky. You need to take escaping\u00a0into account (where <span class=\"lang:default decode:true crayon-inline \">&amp;<\/span>\u00a0 becomes <span class=\"lang:default decode:true crayon-inline \">&amp;amp;<\/span>\u00a0). If you just do the traditional <span class=\"lang:default decode:true crayon-inline \">?a=1&amp;b=2<\/span>\u00a0 parameters, you don&#8217;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?<\/p>\n<p>Yes, for a URL that you visit in your browser, it&#8217;s great that everything is in the address bar. It&#8217;s great that you can bookmark such a search. But <strong>we&#8217;re talking about an API here, not about a page being visited in the browser<\/strong>.<\/p>\n<p>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 <span class=\"lang:default decode:true crayon-inline \">HTTPS<\/span>\u00a0 to something else for better performance, if we want.<\/p>\n<p>An additional advantage is that we can now easily use <a href=\"http:\/\/json-schema.org\/\">json-schema<\/a> both to <strong>validate the incoming requests<\/strong> and to <strong>auto-generate most of our API documentation<\/strong>.<\/p>\n<p>Examples of our calls are:<\/p>\n<ul>\n<li><span class=\"lang:default decode:true crayon-inline \">reservation.create<\/span>\u00a0 to create a reservation<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">reservation.quote<\/span>\u00a0 to get a quote (rent calculation) for a specific vacation home\u00a0and period<\/li>\n<li><span class=\"lang:default decode:true crayon-inline\">reservation.cancel<\/span>\u00a0 to cancel a reservation<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">reservation.list<\/span>\u00a0 to get a list of reservations based on the search parameters<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">property.list<\/span>\u00a0 to get a list of properties (vacation homes) based on the search parameters<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">property.rate.list<\/span>\u00a0 to get all rates of a single\u00a0vacation home<\/li>\n<\/ul>\n<p>All in all, we are very happy with this switch.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you don&#8217;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 &hellip; <a href=\"https:\/\/joost.vunderink.net\/blog\/2016\/01\/03\/why-we-chose-json-rpc-over-rest\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[],"class_list":["post-441","post","type-post","status-publish","format-standard","hentry","category-software-development"],"_links":{"self":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts\/441"}],"collection":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/comments?post=441"}],"version-history":[{"count":4,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts\/441\/revisions"}],"predecessor-version":[{"id":445,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts\/441\/revisions\/445"}],"wp:attachment":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/media?parent=441"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/categories?post=441"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/tags?post=441"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}