gbxelq
Last Updated: March 30, 2017
·
40.72K
· gnclmorais
C740358b2cce341557a23ffb981aacf2

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 the 30 following elements starting from the 0 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

Say Thanks
Respond

13 Responses
Add your response

10008
39639fde05c65fae440b775989e55006

I suggest including http://jsonapi.org/ in references too.

over 1 year ago ·
10018
C740358b2cce341557a23ffb981aacf2

@stas, added! Thank you for the contribution. :)

over 1 year ago ·
10047
489c1040c73d43b4901dff5bc0beed64

I think you meant to say camelCase notation not cameCase when describing naming response fields. I like the tip though :)

over 1 year ago ·
10048

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).

over 1 year ago ·
10066

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.

over 1 year ago ·
10076
C740358b2cce341557a23ffb981aacf2

@hpoom, that's a great tip too! I'll definitely look into that, and update this protip. Thanks!

over 1 year ago ·
10078
C740358b2cce341557a23ffb981aacf2

@adevore3, fixed, thanks. :)

over 1 year ago ·
10079
C740358b2cce341557a23ffb981aacf2

@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.

over 1 year ago ·
10099

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.

over 1 year ago ·
10201
E6a042553edc475cda59f8f62df2a86c

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?

over 1 year ago ·
10244
E6a042553edc475cda59f8f62df2a86c

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.

over 1 year ago ·
10261
E6a042553edc475cda59f8f62df2a86c

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 summarize

This 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 changing

If 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.

over 1 year ago ·
10265
E6a042553edc475cda59f8f62df2a86c

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.

over 1 year ago ·