API design guidelines – best practices for building a user-friendly API

| 6 min. (1252 words)

I’ve seen my fair share of poorly designed APIs – and I’m not alone in experiencing poorly built or clunky APIs that add dead weight to an application. Unfortunately, those who built the offending API didn’t follow API design guidelines and have made life harder for fellow developers.

A robust API will enable users (our fellow developers) to integrate and strengthen software, and if it’s not built robustly you risk the integrity of not only your own application but the software you are integrating it with.  

So what makes a robust API? In this guide, I’ll walk through a couple of core concepts to consider. These concepts are applicable to all types of APIs, regardless of language or type. 

Why are APIs so fragile in the first place? How to not break your API

An API’s purpose is to allow developers to create software to access and interact with your application. This is software-software interaction and allows programs to communicate. As soon as you change your API, the client (machine) will not know how to automatically adapt.

So, if you change your API without considering the implications, your user’s integrated software breaks. Backward compatibility is key in API changes as many applications will be using the old calls and simply changing them will stop those applications from working correctly.

So before you change anything, think of the impact this could have on your applications, or applications integrating with your API.

API design guidelines

It’s your responsibility to create an API that follows good practices. You can’t specifically control what users do, but you should be able to implement guidelines for using your API the best way. There are a few ways you can do this.

Great documentation prevents guesswork for your users

Often, the API isn’t used to its full potential. I’ve actually seen API documentation that is a direct dump of an SQL database table. Not very helpful when the description of a field is describing the type, i.e. “Character string. Variable length” when the fieldname is “ParentId”. I’d much rather know what the parent is.

Good documentation lets your users know what each call does, and how to do the best/most with as little calls as possible.

There are, admittedly, many users that are happy to have a play and having great naming conventions can lead to intuitive self-discovery. However, they should not replace documentation. These require a level of knowledge of your application and the API which you can’t assume all users will have.

And let’s not forget the reduction in support costs from repeat questions!

The API needs to make sense for the context

The terms “chatty” and “chunky” are used to describe an API’s state and are terms coined by Jonathan Hawkins and Emmanuel Schanzer of Microsoft to describe an efficient 3-tier application design (web with to/from connections to legacy systems). So I’ll use their definitions here so you can decide the context.

SDKs and integrations

APIs are basically their own little language that communicates with your system to get certain common tasks done, which is why I’ve included this section as an important part of these API design guidelines.

A good practice for API design is to offer an SDK or integration to your API for a given language. This isn’t vital to success, but it definitely helps the developers working on calling the API as it also abstracts the business logic away from the client’s application and ensures that it’s used in the way that was intended by the API design.

As an example, here at Raygun, we are very lightweight and have integrations built for many different languages to help customers use raygun in the right way. This not only provides an easy way to integrate with Raygun’s API, it also guides users to use the higher level calls and not worry about low-level data objects.

Here is an example of how to access Raygun’s API in an ASP.NET application within the error handler in the Global.asax file: 

protected void Application_Error()
{
  var exception = Server.GetLastError();
  new RaygunClient().Send(exception);
}

Clear status codes

Using correct error codes is extremely valuable to the user of your API, and beneficial to your development team. If you are thinking “but there are over 70 codes! Do I have to use them all?”, the good news is most of them are quite ambiguous – so you don’t need them.

(I’m looking at you 418 I’m a teapot.)

Best practices tell us that we shouldn’t assume people know the context of errors messages, as they may toggle between apps and see your error message days after the incident. Be generous with the information you share so users can make sense of it.

When you have to check the payload for errors instead of being able to rely on the status code, you can waste so much time figuring out what is going on. Errors directly in the payload can break applications that are using this payload.

Example

Status code 200 OK returns, but within the payload, there are lists of errors or an error message. If it used the status code instead (i.e. 500 internal server error), the user would parse the payload differently. This is essential for developers so they can easily handle situations where they have no control over.

400 Bad Request indicates that the request is incorrect and needs to change. 403 Forbidden indicates that the request is not within the scope of the application’s authentication. Each of these codes represents a different situation and helps tremendously for debugging.

try {
 var response = (HttpWebResponse) request.GetResponse ();
} catch (WebException e) {

 switch (((HttpWebResponse) e.Response).StatusCode) {

   case HttpStatusCode.NotFound:
     // code to handle 404 here
     break;

   case HttpStatusCode.ServiceUnavailable:
     // code to handle 503 here
     break;
 }
}

Aim for consistency in your error messages, as it’s not as common as you think, and APIs are rarely as you need them to be!

API design guidelines: considerations for the build process

A few questions that can help gain clarity on your API project are:

Are you ready to build better APIs?

The main purpose of an API is for the users to be able to successfully complete common actions with ease and with as little calls as possible.

If they need to make five calls to simply create a user, then something isn’t quite right. If they get back giant payloads of worthless information, it’s quite possible that these payloads should be split up for different tasks.

If users spend a lot of time trying to debug errors through the payloads, then the requests need to be updated to return appropriate error codes. This saves developer time as they will know what’s gone wrong, rather than trying to debug an issue which is present on the API server rather on the client.

APIs deal with low-level information/objects but should give high-level access for developers to use.

Don’t forget, better APIs make fellow developer’s lives easier!

Now read

Ebook: Do you have a strong process around JavaScript errors? Download the ultimate to JavaScript Error Monitoring here

Did you know Raygun’s best features are available out of the box? Check out how Crash Reporting compliments deployment tracking here.