History of Representational State Transfer
Roy Fielding coined the term Representational State Transfer (REST) in his 2000 doctoral thesis Architectural Styles and the Design of Network-based Software Architectures. In essence, he argues an API should use the existing HTTP verbs, and it should focus on representations of business objects rather than backend implementations. An example might be to model a Customer object the way the business knows and interacts with it rather than how it’s stored on the customer database table. In this post I’ll describe how to design an effective REST API using some of the concepts Fielding pioneered.
Before the REST architecture style gained wide adoption, it was common to use the Simple Object Access Protocol – SOAP. The design of SOAP meant you were using semantics very similar to the underlying persistence stack. That is, the calls you made to the SOAP service were much like the implementation of the service and data layer calls that the service itself used internally. Consequently, any changes to the service often meant breaking changes to the structure of the request and response that your customers use.
SOAP method names reflected the action and the object you were working with. For example, updateCustomer(). The SOAP protocol requires a fair amount of ceremony to setup and use.
Use the HTTP verbs
With REST building upon the foundations of the HyperText Transport Protocol (HTTP), we use the built-in HTTP verbs:
- POST to create a new resource;
- PUT or PATCH to update the resource;
- GET to retrieve it; and
- DELETE to, you guessed it, delete the resource.
The Difference Between PUT and PATCH
Both PUT and PATCH achieve the same end – they update an existing resource. The difference is in what you send in the body of the request. A PUT expects you to pass the entire resource with all its attributes. Even if you are only updating a subset of the resource’s attributes, you need to pass the unchanged attributes too. If you don’t, then by convention the attributes you didn’t specify are set to null.
A PATCH on the other hand only expects you to send the resource’s modified attributes in the body of the request. Any attributes you don’t specify will be left unchanged in the backing datastore. If you inadvertently send in some or all the unchanged attributes, then no harm done. A PATCH does not touch them.
A PATCH means you can send a smaller payload in the request. This can be useful in low network bandwidth situations. On the other hand, a PUT gives you some degree of confidence the resource will indeed end up in the state you expect. Which of the two to use depends on your specific situation and your team’s standards.
Use Resources, not Database Entities
As I mentioned earlier, the focus should be on business objects (resources) rather that database entities. This is helpful because:
- You are de-coupling the business objects from their underlying implementation. This allows you to make changes and improvements to the underlying persistence and data layers without breaking the promise you made to your consumers in terms of the format and structure of the request and response; and
- Using business objects helps you establish a common language or terminology with your business customers. When you are using terms they understand, the nuances lead to a stronger understanding with the business. You both know what each other are talking about.
For much of the API, the business resources will be similar to the underlying entities. After all, a Customer is a Customer, right? Yes, but often there are some slight connotations that make the notion of a customer different among two different business units.
Naming Your Resources
By convention, and for greater understanding between customers and the development team, use the plural form of nouns. So use “customers” rather than “customer” or “createCustomer”. For one, the HTTP verb describes the action you are performing (create – POST, GET – retrieve, etc.). Secondly, we are dealing with the concept of “customers” – retrieving them, creating them, updating them, and so on.
Naming your resources, also known as resource modelling, is the area that requires the most thought. Customers and users of your API will be developing their own applications to talk to your API, so they are naturally expecting very few structural changes over the life of your API. The important concept to remember is you want to move away from simple CRUD (Create, Read, Update and Delete) and toward communicating intent. A POST to /customers/123/addressChanges means creating a new change of address for customer number 123. Prakash Subramaniam of ThoughtWorks wrote a great article that expands on the concept of resource modelling.
The resource is an integral part of the URL. Use nouns, not verbs. Use the plural form of nouns, so when you want to create a new customer, you do a POST to /customers. The full request would look something like this:
curl -X POST https://api.domain.com/customers -d @customer.json
And customer.json would look something like this:
{
"name": "MegaCorp Inc",
"mailingAddress": {
"street": "123 Main St.",
"municipality": "Anytown",
"provinceState": "Manitoba",
"country": "Canada"
}
}
The nice part of this resource representation is that the underlying service maps the individual fields to the database tables and columns. The user of the API is unaware of, and could care less about the underlying database table structures. Effective resource modelling goes a long way to helping you learn how to design an effective REST API.
Versioning Your API
I mentioned earlier that it is important to establish the contract and avoid changing it. But sometimes change is inevitable. So applying a version number to your API allows your customers to use either the current version or the new one, at least until they have migrated to the new version.
Two of the most common ways to version your API are to specify the version number in the URL, or include it in the request/response header. Regardless, strive to update the API version number only for changes that break backward compatibility. If you’re using Semantic Versioning in your applications, this means only changes to the major number should cause a change to the API version number.
Versioning in the URL usually looks like this: https://api.domain.com/v1/customers
where “/v1” is version 1.x.x of your API.
If you choose to use the request/response header to set your API version, you can use a custom header, such Accept-version. For example: Accept-version: v1
. Or, you can tack it on to the Accept header – Accept: application/json;version=1
.
Final Thoughts
In summary, the important items to remember when trying to figure out how to design an effective REST API are:
- Use the HTTP verbs;
- Separate the underlying implementation from the intent you are conveying with your resource model;
- Make that resource model reflect the language and processes of your business customers; and
- Use versioning appropriately.