Deep Dive into building APIs
(Updated 6th May 2019) APIs are becoming the mainstream way of exposing services to the world and it’s known that using a well-designed API is extremely rewarding, while using a poorly-designed API can be very infuriating. However, when we ourselves strive to build the best-designed APIs possible, we tend to trip up a bit. What aspects of an API should be considered in order to come up with a well designed API? What are the available tools to facilitate the API development process? Wait what is an API?
In order to build an API (Application Programming Interface) we need to understand what it is. The terms in the acronym API are defined as follows
- Application (app) in software terms is computer software designed to perform a group of coordinated functions, tasks, or activities for the benefit of the user.
- Programming (coding) is the process of creating a set of instructions that tell a computer how to perform a task.
- Interface is a shared boundary across which two or more separate components of a computer exchange information.
With that, an API can be defined as a set of subroutine definitions, communication protocols, and tools for building software. The API could be for a web-based system, an operating system, a database system, computer hardware or a software library. The APIs contain building blocks that can be put together by a developer to build a computer program.
The effectiveness of an API is determined by it’s usability.
If developers can easily use the API then the API can graciously claim to have achieved its purpose. An API is usable if it accepts straight forward inputs, gives valuable feedback and is predictable. A predictable API is an API who’s users know what to expect. Usability is achieved by implementing the appropriate API design, approriate and consistent data formats, proper documentation, specification and/or definition.
In this post, we will discuss a few considerations in building APIs such as types, styles and various standards. We will delve into:
- API designs
- API data formats
- API specification and styles
- API definitions
- API documentation
- API mocking
- API testing
1. API Designs
API design refers to the API’s pattern or architecture, how the API is made available to its end users. Having a consistent API design and format is important as it facilitates better API maintenance and implementation. It also enables consumers(developers) to easily use the API as it reduces the learning and implementation curves. There are four primary architectural design styles. Though not mutually exclusive, these designs have a number of differences that are important to note.
i. Uniform Resource Identifier Design. This style allows developers to invoke requests through common HTTP operations like POST, GET, DELETE and PUT which are usually mapped to CRUD (create, read, update and delete) operations. An example of its usage is the REST API architecture which allows one-way interaction centered around a Resource commonly referred to as an object. It works best where data can easily be described as objects that can be manipulated within the CRUD cycle.
ii. Event-Driven Architecture (EDA) Design. As the name suggests, this pattern is centered around events. The events are defined at technical and business level. Those events get triggered by specific actions or API calls. What stands out in the Event-Driven Architecture is that it’s a two-way street. Whereas other systems rely on either the client or the server listening and responding to events, the Event-Driven design requires both client and server to listen to new events. One of the most popular examples of the Event-Driven style is Reactive programming popularised by ReactiveX API thorugh the RxJS library.
iii. Hypermedia Architecture (HA) Design. The Hypermedia Architecture is a middle ground approach between the Event-Driven Architecture and the URI Design — whereas the URI design focuses largely on objects and the requests for them, the Hypermedia Architecture focuses on tasks and the flow between them. A message-oriented design. With hypermedia APIs, the messages passed between components contain more than just data. They also include descriptions of possible actions. This means that actions, as well as data, are loosely coupled making Hypermedia API self descriptive. A good example of an API offered as a service that implements this design is Amazon API Gateway.
iv. Tunneling Style. The Tunneling Style is the oldest of these four styles. It functions as a system of Remote Procedure Calls (RPCs). It can be organized in an XML message format transported over HTTP(XML-RPC), JSON message format transported over HTTP(JSON-RPC), or protocol buffers over gRPC. Notabley, gRPC, developed by google, is advertised for use in connecting services in a micro-services based architecture and connecting mobile devices and web clients to backend services. Tunneling APIs allow for localization of content, where RPC calls are used by distant hosts to request access as if the server providing the data is local.
Fun fact
The ‘g’ in gRPC stand for different things with every gRPC release… good, green, glossy and currently at this writing ‘gizmo’ for version 1.17
2. API DATA FORMATS
This is basically how the API handles interaction between data generation and data requests. There are a number of data formats available and popular in the API world. The following are the most common.
i. JSON(Javascript Object Notation)
JSON is based on the popular Javascript programming language built on two structures; a collection of name/value pairs and an ordered list of values.
{
"title": "Json example",
"properties":
{"firstName":
{"type": "string"}
},
"knownValue":
{"type": "boolean"},
"age":
{"description": "Age in years","type": "integer","minimum": 18},
"required": ["firstName", "lastName"]
}
ii. XML(Extensible Markup Language)
XML is a text format derived from SGML. Like json, XML is both human and machine readable. Compared to JSON, XML is a lot more verbose. When XML is written with opening/closing tag pairs, the message body can be significantly larger than the comparable json message. Data in XML format is passed as strings, requiring metadata to describe fields and corresponding data types.
<xs:element name="user" type="lettertype"></xs:element>
<xs:complextype name="lettertype" mixed="true">
<xs:sequence>
<xs:element name="usertype" type="xs:string"></xs:element>
<xs:element name="userage" type="xs:positiveInteger"></xs:element>
<xs:element name="regdate" type="xs:date"></xs:element>
</xs:sequence>
</xs:complextype>
So with the two, which one am I inclined to? well Simba….
iii. GraphQL
GraphQL is not just a data format, but is stated as:
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
GraphQL provides a way to expose resources, evolve the data and retrieve it on an as-needed basis. A graphql API is largely driven by the application’s data requirements. GraphQL touts these advantages over a traditional REST API:
- Allows the client to control what data it receives. Less data means faster responses.
- The client can query for, and get back more than one resource type
- A graphql API can have just a single endpoint and can be evolved with minimal breaking changes
A graphql request/query uses a custom json-like syntax, and returns a json response. The returned data has a similar structure to the request.
{
allFilms {
films {
id
title
director
}
}
}
Request
{
"data": {
"allFilms": {
"films": [
{
"id": "ZmlsbXM6MQ==",
"title": "A New Hope",
"director": "George Lucas"
},
{
"id": "ZmlsbXM6Mg==",
"title": "The Empire Strikes Back",
"director": "Irvin Kershner"
},
{...}]
}
}
}
Response from the graphiql demo.
The custom request structure may require that you use a graphql specific tool to explore or test an API. You can find a comprehensive list of tools here, and see what developers find challenging on this twitter thread. A few of these tools include:
- GraphiQL - browser based GraphQL IDE
- Relay - javascript framework for building react applications powered by GraphQl
- graphql-tools - a set of javascript utilities for building and mocking GraphQL APIs
3. API specification and styles
Having standard data API data formats reduces bikeshedding. However, even with these formats, API designs can get very diverse and different. Whereas this in not neccesarily bad where developers are free to create their own styles for their own convenience, it gets messy when bombarded with all sorts of sytles and sudo formats. However, there are a few defacto styles that have been adopted to standardize the different data formats. For XML, we have SOAP(Simple Object Access Protoco) architecture which is the most popular. For Json, the jury is still out with JSONAPI, JSend and OData JSON Protocol being strong contenders. JSONAPI has seen wider developer adoption, growth as well as community support, so very much inclined to it.
3.1. A Note on REST + JSONAPI
During the design of a REST API, it is very easy to get lost in the ‘how’ of the API’s implementation. This is especially true if you’re the one who will be building out the functionality. As a backend developer, it is only natural that you think about the API based on what data it will handle and how that data will be stored. The problem with this is that our REST resources become coupled with application objects and consequently, database tables.
So, what exactly is a resource? According to Fielding:
Any information that can be named can be a resource: a document or image, a temporal service (e.g. “today’s weather in Los Angeles”), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author’s hypertext reference must fit within the definition of a resource. A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.
If you can name it, it can be a resource! Simple, right? This article is an excellent resource on choosing resources and how to structure URIs.
So, is it possible to have non-resource routes/endpoints? And if yes, how do you handle them? And before all that, is it still a REST API? Layering on jsonapi adds an extra level of complexity. Resources in jsonapi are represented by resource objects:
"data": {
"type": "users",
"id": "101",
"attributes": {
"name": "user name"
},
"relationships": {},
"links": {},
"meta": {}
}
The type
and id
are required for all responses from the API, and have the added constraint that:
Within a given API, each resource object’s type and id pair MUST identify a single, unique resource.
The only time the id
is not required is when creating a new resource on the server - no client-generated ids.
Since the id is sent along in the data
of a PUT|PATCH
this (might)necessitates a change in the URL scheme.
Instead of PATCH /resources/1
this becomes PATCH /resources
with the id extracted from the request body.
That, or we have to accept some redundancy on our endpoints.
The type
and id
are easy enough to populate when our resources map closely to our database tables, things get complicated otherwise.
A common endpoint where this will occur is authentication, where a user expects to sign-in and get an access token.
In this case, the type can be set as ‘token’, but what should the id be? And, does it really make sense to serialize the token into attributes
?
From discussions we have 3 choices:
- We could clobber the token into a resource object as discussed here.
- Ditch the spec and return a flat json object
- Send the token in the top-level
meta
object, which is meant for “non-standard meta-information” (whatever that means). By sending back a response without primarydata
, it implies that the response doesn’t represent a resource.
The JSON:API spec adds constraints to a REST API that amplifies the choice of how we structure our resources. It might also need us to jump through a few hoops to structure the responses just right. While the urge to conform to the spec is high, there are times when breaking out will just make sense.
4. API Definition
API definition, similar to an API specification is a structured description of how the API behaves and what to expect from it in a human-readable format. It provides an indication of how the API is designed and the data types the API supports, without providing access to the source code. The only difference being API definition is meant for machine-consumption. It can be used to generate documentation, SDKs(Software Development Kits) and code samples. The most common API specification formats are Swagger/OpenAPI, RAML and API Blueprint.
i. RAML - RESTful Markup Language
RAML is an extension of the YAML specification for describing REST APIs The yml syntax is easy to read and write and is easily parsed to generate documentation. RAML is an end-to-end API tool providing for design, mocking, testing, documentation, and sharing.
ii. OpenAPI
Previously Swagger, the OpenAPI specification is arguably the most widely adopted specification format. The format is also based on YAML, making openapi specification documents easily readable. Swagger also provides tooling for the API lifecycle.
iii. API Blueprint
API Blueprint syntax is based on markdown, making it easily the most familiar for a developer.Most of the tooling provided around API blueprint is developed by the community.
5. API Documentation
The API documentation is a manual on how to use the API. It is meant for developers who want to take advantage of the api’s functions or services. There are many tools for auto-generating and maintaining API documentation. The generated documentation can either be static or interactive. Here’s a table of a few common ones and sample documentation for each.
Documentation tool | Demo | source |
---|---|---|
swagger | Swagger Demo | Swagger/OpenAPI |
ReDoc | ReDoc Demo | OpenAPI |
DocBox | Wobble Demo | Markdown |
Apiary | Apiary Demo | API Blueprint |
Readme.io | Readme.io Demo | Swagger / OpenAPI Spec |
Gelato | Gelato Demo | Markdown |
API-docs.io | API-docs.io | OpenAPI, Swagger, RAML |
LucyBot | LucyBot | Markdown, Open-API |
6. API MOCKING.
Mocking refers to the process of simulating behavior. API mocking can, therefore, be described as the process of simulating the behavior of an API. Instead of using an actual API, a “replacement” is created. The “replacement” behaves like the original API, but lacks many of the functional and non-functional characteristics of the original component. Why mock an API? There are several scenarios where as a developer one might need to mock an API. Take the example of the development stage of a system we’ll call system Y. System Y might have external dependencies whose point of interaction is an API. The said system may not be available during development for security or other reasons. In such a case System Y’s developer might need to create a mock of the said dependancy’s API in order to proceed with the development process. For testing in cases where again the API is not available or for integration demonstration where exposing the actual API pose security risks. Most API tools such as SoapUI and Postman provide API mocking services along with other services to support the APIs lifecycle. There are, however, other tools both local and online that are dedicated to API mocking.
local tools | online tools | rails specific tools |
---|---|---|
mountebank | mockable | webmock |
WireMock | Mocky | vcr |
MockServer | mockAPI | |
json-server | Sandbox |
7. API TESTING.
API testing as defined by wikipedia is a type of software testing that involves testing application programming interfaces (APIs) directly and as part of integration testing to determine if they meet expectations for functionality, reliability, performance, and security.
Here’s a list of some common API testing tools.