5 Best Practices for Better RESTful API Development

5 Best Practices for Better RESTful API Development

Related: "5 More Tips to Improve Your RESTful API Development"

API development has been a very interesting and challenging type of web development in recent years. Scalability wasn't nearly as important early on in the Internet's history when there were less users, so standalone apps performed well for the most part. With the flood of different mobile devices, however, today's users are now able to use the same app from many different devices, which has resulted in developers needing to scale their apps.

With that said, it makes sense to create a common API that can easily and effectively scale to the ever-changing needs of the user rather than developing different standalone apps. Because there's several different technologies to create APIs, there's no real standard or method for designing APIs. However, there are several agreed-upon best practices for designing APIs correctly. In the absence of a real standard, it's hard to create rigid conventions. However, most of what's going to be discussed comes from real-world experience from moving legacy apps to an API model or creating a new API from scratch that's not only resilient and stable, but that's also scalable and fast.

Importance of Good API Design

No one likes to see a 404 error on a website, especially when someone has bookmarked a blog post or article for future use. It's a hassle for the user anytime a URL needs to be changed, and URL modification isn't good for the SEO, either. Designing an API without the right abstraction might require changing the URLs later. Although redirects can solve this problem, it's a SEO best practice to avoid redirects. So, it makes sense to design the standards before you delve into API design and focus later on the specific details and business names before creating the URLs.

In this series, we'll keep ourselves focused on standards for API development. This series covers different technical standards, which vary depending on the specific business case. The assumption is that the API design is for RESTful services that are HTTP-based.

1. Nouns, No Verbs

The first rule of API design is to use nouns in the URL. We should also delegate verbs using HTTP verbs. For example, the following URL contains the GET HTTP method:

chanderdhall.com/gettrainings

Because HTTP supports the GET method, the API needs to be designed in a way that the standard verb GET is used, which results in the following URL:

chanderdhall.com/trainings

What's the problem with the original URL? Although the URL looks good and is very intuitive, what if a user tries to use this with a different HTTP verb? Let's say the user wants to use the following URL:

POST chanderdhall.com/gettrainings

This is evidently self-defeating. We're trying to add a training, and the URL appears to give us the wrong interpretation, which might confuse the user into the URL's actual use. This same rule applies to the rest of the verbs. You can create a HTTP DELETE request that says GetTrainings as shown in the following solution:

GET chanderdhall.com/trainings
POST chanderdhall.com/trainings
PUT chanderdhall.com/trainings
DELETE chanderdhall.com/trainings

As you might notice, the URL never changes, no matter what the verb is.

2. Two Base URLs per Resource

The next standard is based on the Keep it Simple, Stupid (KISS) principle. We really need two base URLs per resource: one for multiple values and one for the specific value. In this example, we need to either know about multiple trainings offered or we need to find a specific training, given that we have the required information to retrieve it. Again, that information could be the training ID or name. The following shows different results that use two base URLs with four HTTP verbs:

HTTP GET (read)
GET chanderdhall.com/trainings - Retrieves all trainings
GET chanderdhall.com/trainings/123 - Retrieves all trainings with ID=123

HTTP POST (insert)
POST chanderdhall.com/trainings - Creates a new podcast
POST chanderdhall.com/trainings/123 - Error

Why is there an error by using the POST method for a training with an ID? It's because the ID is something that gets generated by the database and then is passed back to the client. Why might you create this type of functionality if all we're going to do is return an error? This is because we're designing an API and we don't want to give the wrong impression to a user that his or her request got accepted when, in actuality, it didn't. So, it's important that we handle errors wherever needed.

HTTP PUT (update)
PUT chanderdhall.com/trainings - Bulk update all trainings
PUT chanderdhall.com/trainings/123 - Updates a particular training if it exists. If it doesn't exist, then it throws an error. It does this because we don't want the PUT method to perform an insert because the POST method is the correct verb for inserts.

HTTP DELETE
GET chanderdhall.com/trainings - Deletes all trainings. Becareful to make sure the person is authenticated and authorized to delete the trainings.
GET chanderdhall.com/trainings/123 - Deletes trainings with ID=123 with authorization permissions. It should check to see if the particular training actually exists. If the training doesn't exist, then it should throw an error stating that the resource wasn't found.

3. Associations

Your APIs should be very intuitive when you're developing them for associations. The following URL intuitively suggests that it's requesting a training that's delivered by a trainer with an ID of 14 and a training with ID 114:

GET /trainers/14/training/114

We have traversed two levels in this URL. One level is the trainer, and the second level is the training that the trainer is providing.

How many levels should we traverse? That's a hard question to answer but it's better to keep it to a max of three levels unless a business case dictates to traverse higher.

4. Complexity

It's highly recommended to use a query string toreduce the complexity of a URL that has several different associations. The following shows how to use a query string that's somewhat complicated and unintuitive to the user:

GET /trainers/12/training/11/zip/75080/city/richardson/state/tx

The following shows a better way to write a query string:

GET /trainers/12/training/11?zip=75080&city=richardson&state=tx

What goes into a query string? There's no hard and fast rule. You can use a Domain Driven Design approach to put the entities in the regular URL before the query string (in this case ‘trainers' and ‘training') and the value objects can be added as a part of the query string (in this case zipcode, city and state). Although value objects are similar to entities in Domain Driven Design, they might not have an ID associated with them. In this case, the address is a value object and assuming our address doesn't have an ID, then the properties of that value object can become the filtering criteria for the query string.

Is this a universal law?

No, it's not. This technique can be helpful in generic cases. However, it's usually your business that determines the filtering criteria, but it's something that you can deviate from.

I would recommend going up to three levels on the parent-child relationships in the URL. If your URL is still complex, then consider adding a query string.

5. Error Format

Error formatting can becoming especiallyinteresting when we don't explicitly define a format. I believe an error format is very important and there's a need to have a particular error format across the board in a company. Let's consider an error format such as the following:

{
    "error_type": "Application Error",
    "error_details": "#500: Internal server error"
}

Although this isn't a bad error format, it's somewhat difficult to use. From the client's point of view, the HTTP error code isn't a different field and needs to be inferred either by splitting the string "error_details" at the level of the ":" and then comparing it with all possible HTTP codes. There's room for error and processing is unnecessary and unwanted at the client level. Here's another error format that I like:

{
                "http_code": "401",
                "message" : "Authentication needed",
                "internal_error_code" : "123",
                "details_url" : http:// chanderdhall.com/wiki/errors/123
}

The property names such as "internal_error_code" can be made shorter such as "code." The reason that these property names are longer are so that they are also self-explanatory. In the example above, 401 is nothing but the HTTP error code and 123 is an internal error code details to which could be found at the details_url. This has an added advantage for the user to understand the error better.

It's really up to you regarding how many HTTP error codes that should be supported. About 10 standard codes should be sufficient. My recommendation is to use HTTP error codes so that it conforms to the standard. Anytime we see a 200 error, then we know that we're successful and so it's quintessential for the user of the API to know the associated HTTP error code.

I hope you liked the Part 1 of best practices for API design. Please let me know if you have any questions by sending me an email. In Part 2, we discuss versioning, pagination, partial responses,  formatted results, multiple responses, search, and hypermedia. 

Feel free to visit my blog for related articles.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish