Introduction
How do you translate traditional API’s into REST? Many applications face this problem. We may already have an implementation that exposes a typical functional API. In the previous post, I discussed some of the essential properties of a REST API. In this post, I’ll take a simple functional API and convert it to REST resources.
A Simple Address Book
Let’s use a simple example of an address book. Although the example is simple, it is interesting enough to spur discussions that we’ll cover in more details later.
The above diagram is in UML. The interface may manifest itself as a set of Ajax calls or perhaps a simple API in some programming language. E.g. as an interface in Java:
interface AddressBookService { public void addContact(Contact c); public Collection<Contact> getAllContacts(); public Contact findById(String contactId); public Collection<Contact> findByName(String name);
public Collection<Contact> findByAreaCode(String areaCode);
public void removeContact(String contactId);
public void updateContact(String contactId, Contact contact);
public Call callContact(Contact c); public Call dialNumber(String digits); }
We also need some data structures to carry the information (often called DTO’s). If we stick to Java as our language', we would have to build some JavaBeans that defines the Contact and perhaps also the Call.
The Challenge
We want to convert the address book into a set of REST resources. The question is now, what are the resources?
You can think of the REST API as an adapter that converts the user’s conceptual model to that of the implantation. Our language for describing the user’s conceptual model is the definition of resources. Our challenge is to find the most logical resources from a user’s perspective.
The Obvious Resource
The “contact” seems like an obvious resource. The address book has a set of contacts. Can we define all the functionality of the address book service into simple REST calls around a “contact” resource?
If we decide to create a resource around contacts (or more correctly, a resource-set contacts with resource instances such as “John Doe, 512-555-8989”), what functionality do we cover?
Let’s explore this with a simple table where map REST methods on the contact resource to the original API. Notice that I’ve used ‘..,’ in the URL. The ‘…’ is worth a new blogpost by itself. At PayPal the ‘…’ would be ‘https://api.paypal.com/v1/addressbook'. I’ll just use ‘…’ to indicate ‘some base URL that precedes the resource name’.
CRUDF | URL | Verb | Original Interface |
---|---|---|---|
Create | .../contacts | POST | addContact |
Read | .../contacts/{ID} | GET | findContactById |
Update | .../contacts/{ID} | PUT | updateContact |
Delete | .../contacts/{ID} | DELETE | removeContacts |
Find | .../contracts | GET | getAllContacts findContactsByName findContactsByAreacode |
As you can see, most of the methods in this interface had straightforward mappings into REST. However, we’re still left with a few methods (and consequently also features) that have not been mapped and do not have an obvious mapping.
How About the Calls?
The two methods that we did not map are:
- callContact
- dialNumber
These methods do not have an obvious resource. Or more correctly, they may map to obvious resources in an experienced REST designer’s mind, but it is very likely that if we had several REST experts there would be at least 2 different opinions of the obvious design.
Here is a set of strategies that you may see:
- Controller interface
- Conceptually this would perhaps be seen as a method sent to a contract
- Assuming I wanted to make a call to one of my contacts with the id 1234, the REST call may look like this:
- URL: …/contacts/1234/call
- Verb: POST
- Body: Empty
- Separate resource for call
- For this design, we are asking the clients to create a new resource-instance every time they call
- We could call the resource “calls”
- To make a call to a contact, we would a POST to the “calls” resource. E.g.:
- URL: …/calls
- Verb: POST
- Body: { “contactId”: 1234 }
In our example, I would argue that the approach of separate resources is superior (although I know that that may be controversial). The hint to why I think so is that we also have a method for calling an arbitrary number that can now be easily mapped. I also think this approach is more extendible to possible new requirements such as:
- All calls to a previous contact
- URL: …/calls
- Verb: GET
- Query parameters: ?contactId=1234
- All calls to an area code:
- URL: …/calls
- Verb: GET
- Query parameters: ?areaCode=512
The ultimate judge on which is a better API is your clients. The question you have to ask is what is the most logical/easy to use model for them!
Other Interesting Discussions
With the introduction of the “calls” resource, we have mapped all the original features of the original API. There are some other questions that we could have explored that may have led to a different design.
- Can a client have more than one address book?
- If YES
- Can a contact be shared across multiple address books?
- If NO
- Would it be better to design the contacts as sub-resources of the address book?
E.g.: …/address-books/12345/contacts/1 - Is a call in the context of an address book
- Can address books be shared?
- Can we do more than call a contact? E.g., send SMS, email, etc.?
- If YES
- Are the SMS/email/etc. somewhat similar to a call (in other words, should we perhaps have a more abstract resource called “reach-out” that encompasses calls, emails, SMSL etc.)
In a later blog post, I’ll discuss how to explore these questions by building domain models. However, for this post I’ll keep it simple. The design we have suggests the following:
- The client has exactly one address book
- We’re interested in calls as separate resources
It is important to note that the answers to the questions may have led to very different designs. Also, since REST API’s are often exposed to the public, it is important to think about future extensions before you release an API, hence, the questions above should be explored with the future in mind.
Did We Improve the World for Our Clients?
All we have done is to map the existing API into new calls. We could easily have exposed the functional API as a set of Ajax calls. However, I would argue that we have gained a few very important advantages by converting the interface to a REST style:
- We now have a shared model with our clients.
- We have introduced a consistency into the design. A consistency in abstraction level and a consistency in how the API works.
- With proper REST resource design, we also have a clear and predictable path for future extensions of the API.
Documenting the API
A great side effect of the consistency of REST API’s is that clever open-source developers have provided a set of tools that makes it easy to expose API’s. Although it is outside the scope of this post to discuss these tools, I’ll explore these tools and discuss how they work.
The first tool that I’ll discuss is Swagger. Swagger is a tool that allows you to create a JSON based specification of your resources and the methods you expose. The specification can be used by Swagger tools to generate a web-interface that acts as the documentation portal for your API. The API portal also allows your clients to test out your API without having to write any code.
Another tool worth mentioning is RAML. RAML works in a very similar way to Swagger. It arguably has a nicer authoring environment.
Perhaps in a later blog post I’ll highlight some other options for how to define REST API’s. At SciSpike, we have developed some tools that we are about to release to open source that I believe provide an even nicer authoring environment.
Guidelines for Converting Functional API’s to REST
Our example is a little too simple to justify all the guidelines below. I will come back in later blog post to justify the list:
- Functional interfaces can NOT be automatically converted to REST. Expect a complete redesign when moving from traditional API’s to REST.
- Always reflect your clients view. The name and the number of resources you create should be largely driven by what your client wants to see.
- Interfaces should be durable. Changing an interface may be a big deal and may be very costly.
- Interfaces should be extensible. Evaluate what are likely changes to the interface over time. Can you expand the features without having to change the existing API?
- Interfaces should be well documented. In the REST world, you have no excuses for not providing good API documentation (e.g., with Swagger or RAML).
- Build a domain model. Expect some part of your interface to be easy to translate (those are typically directly related to CRUD(F)) whereof other parts require significant efforts to find the correct resources. The missing resources cannot be found without a domain modeling effort (I’ll explore this in my next post).
- Avoid controllers. Often controller API’s are introduced where another ‘event-based’ resource would be more extendible.
Conclusion
In this blog post, I’ve discussed how to convert traditional functional API’s into REST API’s. In a traditional API, there are usually some obvious resource candidates and the methods on the API can easily be mapped to the CRUDF options for the resources. However, some of the features of your API may be more difficult to map to REST. In later blog posts, we’ll explore some of the most difficult resources. One of the great advantages to REST is that in converting your API the implementors and the clients of the API share a common model at a consistent level of abstraction and with predictable ‘method calls’.
View comments