Simple rules for a sane RESTful API design
The API's job is to make the developer as successful as possible.
This is a collection of some simple principles I find helpful to achieve sane and unobstructive RESTful APIs.
I must stress that some of the statements I'll present are strictly personal, conclusions I reached through my (limited) experience in this area. Also, I assume the response comes in JSON.
You shall use the correct HTTP status codes.
A good API should always attempt to return appropriate HTTP status codes for every request. This way, responses to simple requests (like setting a value to true
or false
) can be defined just by status codes, no additional data involved. Check out the status codes here.
All response fields shall start lowercase, following a camelCase notation.
Enforcing a clear naming convention is critical, and this one obeys JavaScript conventions and it is familiar in Java and Objective-C. So make something like this:
{
"weatherCondition": "sunny",
"weatherTempC": "25",
"weatherTempF": "77"
}
If a field has no value, it shall be null.
This one is quite trivial, but is has a few nuances. Non existing numbers, strings, and booleans are usually represented as null
. But string fields without value should also be represented as null
, not ""
.
Note: Empty arrays shall not be null
. If a field is some kind of list and it is represented by an array, an array shall be returned, even if empty. This makes front-end developers’ work a lot easier.
Example:
{
"id": 16784,
"name": "Lorem Ipsum",
"age": null,
"relatives": [], // no relatives
"address": null
}
A field type shall be boolean if its value is binary.
Don’t use 0
and 1
, don’t use ”0”
and ”1”
, and don’t try to come up with a better solution. Use what you have: true
and false
. It’s universal and objective.
A response shall use HTTP relevant header fields whenever possible.
For examples, responses should have a correct Content-Type
, like application/json
.
A response shall not have a service/endpoint description.
This is redundant. You are not crawling through an API, you are not asking “So, what is this response for?”
You know what you requested and its endpoint, you are aware of what you are expecting, and you are ready for it. Thus, you don’t need a field telling you which service or endpoint you reached.
Also, this maintains a cleaner response, removing superfluous fields and unnecessary encapsulations. So make sure you don’t do something like this:
{
"service": "randomservice:api",
"response": {
... //stuff
}
}
Now, some personal preferences:
You should always include an array of errors in the response.
Even if no errors occurred, you should include an array of errors. A successful response is an absence of errors, thus an empty array. When handling the response, you will check first for the presence of errors, and only then you proceed.You should paginate your responses
Some requests can be really long and heavy, like listings. And sometimes you don’t want the whole list, just some parts of it. So it is wise to follow a strategy of defining the start and end of a request. Example:?limit=30&offset=0
will request the30
following elements starting from the0
index element.You should provide partial responses
What if you just want to request updates of a particular field of a complex entity? There should be no need to request all of the info... So adding a field specifier is good practice. Something like this will receive only the requested field:?fields=name,age,weight,
.
References
Written by Gonçalo Morais
Related protips
12 Responses
@stas, added! Thank you for the contribution. :)
I think you meant to say camelCase notation not cameCase when describing naming response fields. I like the tip though :)
Not sure what your use case might be, but returning an array of errors with every response makes little sense to me, and is definitely not RESTful (as errors are not part of the resource representation). You should use HTTP status codes to indicate an error, and return the details in the response content (as many APIs do).
Some great tips there. Only thing I would as is don't do pagination with limit and offset. Instead use a 'next' and 'previous' and use and offset. Either time off or ID offset. See some of the conversation here it explains better then me.
http://stackoverflow.com/questions/13872273/api-pagination-best-practices
With constantly updating data limit and offset will mean missed data when moving from page to page.
@hpoom, that's a great tip too! I'll definitely look into that, and update this protip. Thanks!
@adevore3, fixed, thanks. :)
@odyniec, yes, that's a very good approach. However, I've had some cases where that approach is not enough. When you want a more descriptive scenario about what's wrong in the server-side, experience showed me this to be a good approach. But it's clearly a personal preference, I must stress.
Some good general points here which I like.
Agree with @odyniec - errors do not represent a resource. I would question your need for them in responses - this to me feels like an anti-pattern. Considering errors should be part of a 4xx or 5xx response, the status code should be enough to let you know.
Also stipulating casing in API responses... let's leave style out of such tips and equations (they serve nothing other than to start debates). This point should have been in personal preferences.
I think you missed out on an important REST principle. The API SHOULD communicate endpoint(s) to deeper level resources. Imagine a list of categories, where sub categories exists. By reserving a field for "next level" resource, the data or server can alter or introduce new levels with zero change to the client code. Such a field MUST be in the format of a hyper link. Like all other REST endpoints.
I can dig up the standard text if you like?
I call the field uri in the example below. In the original REST text it's called source link.
{
"list": [
{
"name": "ACME Books",
"type": "list",
"uri": "http://acme.com/books/"
},
{
"name": "My book #1 - ACME Books",
"type": "product",
"uri": "http://acme.com/books/mybook/1"
},
{
"name": "ACME Toys",
"type": "list",
"uri": "http://acme.com/toys/"
}
]
}
The list is pretty straightforward. name could be displayed, type give us a way to add different visual representation of the resource and the uri is the source link to the resource. An implementation of the client, using Handlebars, could be:
<script id="acme-list" type="text/x-handlebars-template">
{{#each list}}
<h2><a href="{{uri}}">{{name}}</a></h2>
<div class="acme-element-{{type}}">Lorem ipsum</div>
{{/each}}
</script>
note that this implementation will leak memory and has other issues as well
// any REST client MUST have at least one known
// endpoint to the server
var RESTendpoint = "http://acme.com/";
display( html( JSON.parse( getData(RESTendpoint ) ) ) );
function html(data) {
var source = document.getElementById("acme-list").innerHTML;
var template = Handlebars.compile(source);
var html = template(data);
[].forEach.call(html.getElementByTagsName("a"), applyClickEvent);
return html;
}
function applyClickEvent(a) {
a.addEventListener("click", function() {
display( html( JSON.parse( getData(this.href) ) ) );
}, false);
}
function getData(uri) {
// do a request based on the uri
return data;
}
function display(html) {
document.appendChild(html);
}
The above, simple implementation has no restrictions to how deep it can follow a data diagram/graph on the server. There is actually no logic that tells the code to stop at any point. If you add another layer (e.g. in the database on the server) the client will happily render this new layer too. This is just pseudo code, so don't despair if you can't get it to work.
About Versions
I want to add a citation from Howard Dierkings article Versioning RESTful Services.
If I did a lot of this sort of resource versioning, it is very possible that I could end up with an ugly-looking URL space. But REST was never about pretty URLs and <strong>the whole point of the hypermedia constraint is that clients should not need to know how to construct those URLs in the first place</strong> – so it really doesn’t matter whether they’re pretty or ugly – to your client, they’re just strings.
So to summarizeThis post ended up being longer than I had planned, so here’s the summary.
In REST, the contract between clients and services is the uniform interface
How you version depends on what part of the uniform interface you’re changingIf you’re adding only, go ahead and just add it to the representation. Your clients should ignore what they don’t understand
If you’re making a breaking change to the representation, version the representation and use content negotiation to serve the right representation version to clients
If you’re changing the meaning of the resource by changing the types of entities it maps to, version the resource identifier (e.g. URL)
While a little light on how <cite>Versioning a representation over an existing media type will look</cite> like, I tend to agree on his assessment.
Further resources
I often use this diagram from Ruby's Webmachine when deciding the correct path for REST responses.
I find this article, about modeling RESTful representations in JavaScript, food for thought, as a concrete example of REST data exchange.