Itโs easy to get sloppy in your APIs, so hereโs a refresher on how to be correctly RESTful in your client scripts
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:
- HTTP
GETshould be used for all retrieval. It should never be used to create, update, or do things. - HTTP
POSTshould 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, usePOST. - HTTP
PUTshould be used for updating โ meaning replacing a collection with different data. The URI should have existed before. - HTTP
DELETEshould be used for deleting.
Following from those basics:
- HTTP
GETshould never create anything or have side effects.GETs can be cached, so your system needs to tolerate this. - HTTP
POSTshould not be required to get or update something en masse. - If you
POSTsomething to a URL, you should be able toGETit from that URL. If that thing is a collection, you should be able toGETit 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/charactersshould allowcurl http://localhost/myservice/charactersto return that whole array, butcurl http://localhost/myservice/characters/1should return just{"id":"1", "name":"Don Draper"}.DELETEshould work the same way, to hit the collection or the item. - Never have verb URLs like
/addNew. The HTTP stuff provides the verbs. - Use
POSTfor 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 usePOST.
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.


