Andrew C. Oliver
Contributing Writer

The rules for REST: How to be RESTful in HTTP/JSON APIs

how-to
Jun 29, 20174 mins
APIsSoftware DevelopmentWeb Development

Itโ€™s easy to get sloppy in your APIs, so hereโ€™s a refresher on how to be correctly RESTful in your client scripts

3 legal law books
Credit: Thinkstock

Recently, I was trying to write a little client script for Apache Solr. It was just supposed to add some data, retrieve it, and delete it. I thought Iโ€™d just do obvious things. But as it turns out I actually had to read carefully.

You see, if I post to http://localhost:8983/solr/my_collection/update/json/docs a JSON document {"id": "1","title": "Doc 1"}, it shows up somewhere other than http://localhost:8983/solr/my_collection/update/json/docs/1.

Solr is essentially a search engine. Solr has a HTTP/JSON-based API. However, it isnโ€™t exactly a REST API because it doesnโ€™t really follow any of the rules for a well-defined REST interface. I whine about this sometimes. Maybe one day if I have time Iโ€™ll even fix it. (I work for Lucidworks, which does most of the development of Solr.)

But my script experience reminded me that RESTโ€™s rules (okay, guidelines) are there for your protection. They make it really easy to write to your API, and they make it behave in a predictable manner. If you really follow these rules, you can almost mount the thing youโ€™re writing the API for as if it were a drive.

Here are the basics:

  1. HTTP GET should be used for all retrieval. It should never be used to create, update, or do things.
  2. HTTP POST should be used for creating. It shouldnโ€™t be used to update or get a resource. If a URI had never existed before now and youโ€™re going to create it and make it hold some data, use POST.
  3. HTTP PUT should be used for updating โ€” meaning replacing a collection with different data. The URI should have existed before.
  4. HTTP DELETE should be used for deleting.

Following from those basics:

  1. HTTP GET should never create anything or have side effects. GETs can be cached, so your system needs to tolerate this.
  2. HTTP POST should not be required to get or update something en masse.
  3. If you POST something to a URL, you should be able to GET it from that URL. If that thing is a collection, you should be able to GET it from a subcontext of that URL. For example: curl -X POST --header "Content-Type: application/json" -d '[{"id":"1", "name":"Don Draper"}, {"id":"2","name":"Betty Draper"}, {"3":"Joan Holloway"}]' http://localhost/myservice/characters should allow curl http://localhost/myservice/characters to return that whole array, but curl http://localhost/myservice/characters/1 should return just {"id":"1", "name":"Don Draper"}. DELETE should work the same way, to hit the collection or the item.
  4. Never have verb URLs like /addNew. The HTTP stuff provides the verbs.
  5. Use POST for appends. But thereโ€™s a bit of a hole to watch out for: What if you have a collection with 1,000 items and you want to add 500 but not rewrite the 1,000? In general, just use POST.

For results, HTTP has return codes. These are the basic ones your services should return:

  • 200: Done, it was okay. Generally, your GETs return this code.
  • 201: โ€œDone, and created.โ€ Generally, your POSTs return this code.
  • 204: โ€œDone, and no body.โ€ Generally, your DELETEs return this code.
  • 400: โ€œClient sent me junk, and Iโ€™m not going to mess with it.โ€
  • 401: โ€œUnauthorized, the client should authenticate first.โ€
  • 403: โ€œNot allowed. You canโ€™t have it because you logged in but donโ€™t have permission to this thing or to delete this thing.โ€
  • 404: โ€œCanโ€™t find it.โ€
  • 410: โ€œMarked as deleted.โ€
  • 451: โ€œThe government made me not show it.โ€

If you allow the client to specify โ€œhow muchโ€ or to filter the results, that should be done via query string parameters. For example: ?page=1 or ?q="name:*Draper". This shouldnโ€™t be inherent in your API. It is fine to prevent the client from hurting itself by erroring if it asks for a whole collection and that collection would return a million rows.

Finally, think about the future. Consider explicit versioning such as in http://localhost/myservice/v1/characters so that when you inevitably break something and have a new version of your API, you can either be backward-compatible or error out and warn the client as to why it canโ€™t get in any longer.

In summary, your API should look nothing like Twitterโ€™s crap โ€œRESTโ€ API.