A system API is a blanket on an existing system. It talks to the underlying system in its language but to the outer world, it presents a standard rest interface. Thus, encapsulating the protocol and technology used by the underlying system from the end-user.
In this read, I have attempted to highlight basic features that should be present in every System API. A poorly designed System API will always need enhancements or other process/experience APIs built on top of it, to get the best out of it.
I will do this by taking an example of designing a database System API. The use case here is to build a database System API in which the database contains an employee table that has over 30 fields. Let’s get started.
Returning fields on demand, Not by default — Field Selector
Coming from our example of Employee Database System API(we will call it EDS API going forward), the get operation for employee records(/employees) would usually return all the fields in the employee table.
But what if the user does not need all the fields? The employee table has close to 30 fields and returning all of them would increase the memory required and network load on the database, the System API and the API caller’s system. This will not have a major impact when the number of records returned is limited. But when someone would try to pull huge set of records via the System API, there will be a network and data overhead.
API caller should be given an option to choose which fields he would want to retrieve. This can be accomplished in many ways e.g. having a query parameter called fields where all the required fields can be passed as a CSV.
This type of design will help the user choose from the list of fields required instead of getting everything at once.
For our EDS API, the underlying database can have millions of records. How can one decide how many records should be returned when ‘/employees’ is hit. Adversely, attempting to return all the records at once can cause the EDS API and underlying database to crash.
Pagination helps return records in batches. Think of it as a book where at one time you can see at max two pages. All the pages in a book have approximately the same number of words and you can navigate between them using the page number.
Similarly for an API, the number of records returned can be paginated i.e. returning only a certain number of records in one call and give the option to iterate over them. This can be implemented using limit and offset.
This would return only 70 records in total and skip the first fifty records i.e. return records 51 to 120. A user can choose to iterate over records as he wishes. Another important part of implementing pagination is returning the total count. Without the total, API caller will not know how many iterations he needs to make in order to pull all the records.
Let the caller choose API response data type
REST APIs usually work on JSON and SOAP services work on XML. Although everyone is slowing moving from XML to JSON, there are still some systems that need data in XML format. Why build an API which is rigid in the type of data it returns?
This can be implemented using ‘Accept’ HTTP header. API should be capable of return JSON when
and return XML when
Accept headers do a great job, but they are standardized and may not be very flexible with a wide variety of data types. There might be a case where you would want to return a CSV or a flat-file or plain text or a format custom to you. To solve this problem, there is another way of choosing a response type using a query parameter.
Having a query parameter called e.g. “type” can help achieve customization to any extent include a custom datatype.
Pattern and Data Validation
API resources can receive data in various forms. Let them be data in a POST request, URI parameter, query parameter or HTTP headers. Whenever there is a clear description available for the pattern or data type, validation should be implemented.
Pattern and data validation stops incorrect data right at the beginning. It is way more efficient to stop a bad request at the contract level(RAML, Swagger, etc.) than to stop it at the implementation level.
Coming from EDS API, a resource which returns details of a particular employee can look like this:
If the pattern for employeeId is:
<countryCode><year of joining><month of joining><random number>
This should be put in the API contract to make sure every employee ID is verified before it reaches actual implementation.
In-built ‘Authorization’ for resources
Almost all the APIs ever built have authentication built right into them. But what is generally missing in System APIs is an authorization. To clarify the difference, ‘authentication’ is finding out if a user is allowed in the system or not and ‘authorization’ is finding out if a user has the appropriate level of access to use the resource.
A good example of this would be the employee API we have been discussing until now. e.g someone wants to build a dashboard with demographics of all employees. Now for this requirement, the dashboard developer needs only read access to /employees resource. /employees resource can have multiple methods like POST, GET, PUT etc. Authorization can restrict access to only GET in this case and block POST and PUT calls.
System APIs are built to encapsulate the underlying system from the end-user and eventually are consumed by multiple other APIs and end systems. This would mean that strict control over who can access what should be there. The authorization makes sure that the API can be used efficiently and securely by all end users.
Hope you found this article interesting, do drop us a comment below with your inputs, views, and opinions regarding Designing a System API
Also, if you are interested in learning more about an exciting new code quality product that reduces your Mule project costs by 79%, follow the below link :