The Modern API
The Modern API
Join the DZone community and get the full member experience.Join For Free
The State of API Integration 2018: Get Cloud Elements’ report for the most comprehensive breakdown of the API integration industry’s past, present, and future.
The concept of the API is as old as programming itself. Documents defining a series of methods, or services with a given input and output are ubiquitous in programming. Today, APIs are the defining mechanism for interacting with services on the web: whether you’re logging in to Google, tweeting on Twitter, finding Facebook friends, this can all be done through the services that are defined in their respective APIs. How is it that these and other APIs come to be? How is it that services can be altered, updated, or removed altogether? At DataXu there was much discussion about how to best implement our API, and at PlanIt I took some of the results of those discussions and implemented them in the form of a RESTful API written in Ruby on Rails.
There are many good reasons to create an API for your users and/or customers. Defining a set of services that can be relied upon to take some input and return some output, unaltered, acts as a virtual contract between consumer and server. Developing to this standard allows the creation of clients based on documentation alone. This means that a live endpoint is unnecessary to development and testing. Tests can be written using stubbed calls, those that are short-circuited, returning a static value instead of a dynamic one. In the context of PlanIt, a mobile app in which the mobile side is written in Xamarin, all of the calls out to the API are done in a single place, whether developing for Android or iPhone.
The contractual nature of the API means it can be trusted to be unchanging for the current version of the API, mitigating the possibility of introducing breaking changes to existing services. At DataXu, the decision was reached to make only non-breaking changes in a given version of an API and the next version (for this discussion we'll start at V0). At PlanIt I chose to include the addition of features (e.g., additional inputs to a service) assuming the old ones remained unbroken. In the next version of an API (V1) additional services can be defined and services that will be changed to a greater extent can be marked as deprecated, but these services will continue to be officially supported in V1. In the version after (V2) we can implement the breaking changes we earmarked in V1. To demonstrate, imagine what would happen if Facebook released a new version of the API with a new definition of their "friend" service, or if Twitter decided to change the definition of their “tweet” services; overnight many thousands of applications would be broken.
This is especially important in the case of multiple clients; when only a single consumer will implement the calls to the API (e.g. a website) we can generally expect the consumer and server to remain synchronized. In the case of many consumers, e.g., applications that implement the calls to the Facebook API, we can expect that some consumers will not have the ability or desire to upgrade as soon as the latest version of the API is released. In the context of mobile users, they have the power to choose when to update their applications, which will leave portions of your user-base behind by at least one version. The mobile developers may for whatever reason choose or be required to leave one of the platforms behind as well; a good example is that the iOS version of your application will require approval, and therefore may lag behind the Android version by some days or even weeks. At the opposite end of the spectrum there are consumers that always want to use the latest API; these tend to be the "primary" clients of the API, or those that are implemented by the same organization as the API itself. Logic dictates that the newest version of the API should be the default if none is specified.
There's an on-going debate surrounding how a client should specify a version of the API to the server, which can be summarized as being in one of two camps. The first camp headers are heavily utilized; the extreme version of this is complete HATEOAS implementation, in which all relevant actions to the one being taken are returned as hypermedia with each call. In the second camp versions are specified in the URLs themselves (e.g., https://host/api/v0/service), as in service oriented architecture. The major difference here is that by using HATEOAS, the consumer will never need to know about changes to service endpoints, but rather by knowing where to look for the endpoint in the associated hypermedia it can simply adapt to those changes on the fly. My personal preference is to use the URL in the standard SOA style, in the belief that clients should generally be explicit in their choice of version.
With great power and flexibility must come great documentation. The PlanIt API documentation describes more than 40 calls, some of which are already marked as deprecated in V1. I'm in favor of having a document devoted to each API that also describes the delta between the previous version and the current one, linking and highlighting as deprecated where appropriate. Each service in the API is ideally separated by category (in my case representing controllers), with the method (CRUD is ideal for RESTful APIs), the expected inputs (and input formats), the expected outputs (with output formats), a description, and any relevant notes.
The choices you make for design and implementation of your API is disconnected from your choice of technology. Crafting a contract through your API creates trust, and enforcing that with best practices regarding versioning creates stability. By following these tips, you can create a maintainable API and simplify implementation and updates for API clients.
Opinions expressed by DZone contributors are their own.