API Scoping and Design Primer

On this page

Purpose

This document discusses some key considerations and best practices when defining an API along with its functional scope and design. It covers the following topics:

  1. What are good use cases for APIs vs inappropriate use cases?
  2. How much functionality should you put into a single API?
  3. How do you deal with composite APIs?
  4. How do you manage sizing and scaling?

API Use Cases

APIs are best suited to synchronous interactions where a request and corresponding response occurs within a very short period of time (i.e., less than a couple of minutes) and as a single transaction.

Data APIs

CRUD (create, read, update, delete) functions on small datasets (i.e., less than 1MB) are ideally suited as APIs. Queries against reference data or open data sets are also great candidates. Queries which can potentially return a large amount of data can be implemented as APIs as long as techniques such as pagination and data segmentation are applied so the responses don’t time out. Appropriate API message or pagination sizes are dependent on the general performance of the network and the back end query systems along with HTTP timeout values on consuming systems. Generally, API messages should not exceed 10MB within the enterprise network and be limited to 1MB for those consumed over the internet. Performance testing should be carried out to fine tune message limits and pagination sizes.

Creation and update of large amounts of data are not appropriate for APIs, nor are scenarios where the data creation or update is against a complex relational model where integrity issues can cause the transaction to fail.

For example, adding account transactions as they occur is a good use case for an API. Adding a day’s worth of transactions to a ledger would be better performed by a scheduled batch process. Providing read access to specific data records is a common use case and can be seen throughout many open data offerings.

Synchronous Transactional APIs

APIs should be used to carry out a transaction only when a response is expected, ideally in real time. APIs ultimately require both the consumer and provider systems to be available for the transaction to succeed and will result in busy retry or busy poll loops when the provider system is down. Scenarios where a response is not required such as fire-and-forget or notification type transactions should be implemented using asynchronous messaging instead of APIs. This allows the overall transaction to be more resilient to outages, which is particularly important when the consumer and provider systems are managed by different teams/organizations, have different SLAs, or reside in different facilities.

For example, when a sales order gets committed to the financial system and a sales invoice is returned, an API would provide a response to confirm the invoice information. Data collected from autonomous sensors such as weather sensors are not well suited to APIs. The sensors do not want to wait for a confirmation that the API has received the data. Instead, these sensors should send asynchronous messages to a queuing system. That way the sensors can continue to provide data while downstream services can consume the data and process it at their own speed.

Asynchronous Transactional APIs

APIs can still be employed when the response is not in real time (e.g., workflows, long running processes, manual intervention). In the cases where a response cannot occur within a HTTP timeout window and where the overall transactional load is relatively low (e.g., a few transactions per second), a callback API or webhook should be implemented by the consuming system. Callback APIs or webhooks give the API provider an endpoint to send the response back to once the transaction is complete. Static endpoints whereby the callback or webhook URL is defined as a configuration for each API consumer should be implemented for protected APIs. This prevents the risk of intercept attacks to redirect the response back to an unauthorized endpoint. Dynamic endpoints whereby the API consumer passes the callback or webhook URL as a part of the request message can be used for APIs where the consuming systems are numerous and constantly changing and where the data is either public or of low risk (e.g., Open Data).

An example of such a long-running transaction would be a complex query that searches multiple data sets on multiple fields. This query can be submitted to an API and receive a webhook to monitor for the final response.

High volume (i.e., hundreds or thousands per second) asynchronous transactions (e.g., credit card payment processing, social media tracking) should be implemented via request-response message queues, which are better engineered to handle high throughput scenarios. In these scenarios, the response queue is monitored for messages that contain a specified identifier to designate the response for a previous request.

Business APIs

Exposing a business system’s functionality as an API is one of the most powerful ways to create more integrated business processes and to foster digital economies. Any business process which can be triggered by another system is a good API candidate. An API to trigger the process with the mandatory inputs as the request schema is a good starting point.

A good example of business systems are claims. A claim can be submitted by an API, kicking off the business of handling that claim. The API submission can create a record and send a notice to a claim officer that they have to begin the process. Perhaps inspectors need to be sent out to evaluate validity of the claim. This approach also opens the way for future automation, allowing the API to call out to other services. A third-party estimator can be consulted before a claim officer even opens the file.

Technical and Utility APIs

APIs can be deployed to simply provide decoupling points within a given system. This is actually the most common use case and forms the foundation of most Cloud-based solutions. Any time a class or entity can be invoked from a remote system (e.g., another VM, container, server) or even another application running on the same system, it could benefit from being exposed as an API. The trade-off consideration is to weigh the additional effort of developing and maintaining the API against the need to isolate a particular application module and the potential for deploying and scaling it separately from the rest of the application. Microservices architecture and SOA are predicated on this concept.

These APIs are commonly used in platform-as-a-service (PaaS) products to allow integration with other systems. Being able to access platform functionality via API enables organizations to connect their legacy systems without having to rebuild them entirely on a new platform. In many cases, these platforms have functionality to automate schema mapping, eliminating the need for legacy systems to alter their request or response objects to align with the new platform.

Scoping Your API

Determining how much functionality should be placed into an API is a balance between reasonable complexity and minimizing the number of interactions to accomplish something of value.

Entity Based

The simplest approach to keeping API complexity in check is to limit the scope of an API to a single business object or entity (e.g., client, facility, permit). REST principles align an API to a resource, which also roughly equates to a single business object or entity. When designing a new system, applying Domain-Driven Design methods is an effective way to identify good granularity and scope of APIs.

Access Control Based

Access controls and permissions are more easily managed at the API level rather than the per operation level. Therefore if an API has very different security profiles (e.g., a large group of consumers with read only and a much smaller group with create/update/delete), it would be better to implement separate services for each security profile. In this example, a read-only API can then be deployed and accessed via a different network segment and allow for a simpler authentication scheme while the more privileged version of the API would be deployed into a more protected network segment and require more robust authentication.

Consumer Context Based

APIs, especially Business APIs, can often have different execution contexts for different groups of consumers. For example, an API for account management may offer end users the ability to self-manage profile information while offering administrative users the ability to manage access control and system privileges. In this example, the functionality should be split into a user self-service API and an administrative user management API. Trying to place all the conditional logic into a single API would make it unnecessarily complex, especially since there is little to no possibility of a single business process needing to interact in both consumer contexts at the same time.

Composite APIs

Composite APIs act as aggregators for multiple downstream APIs. These APIs are commonly found in a microservice architecture where data and functionality is maintained by smaller services. Composite APIs can be essential in reducing complexity when business needs to touch on multiple different back-end systems. These APIs are not restricted to workflows or orchestration engines. They simply bring together business components and facilitate that driver of value: reuse. Just as with a conventional API, it is essential that a composite API be targeted in its scope and approach. A composite should not simply mash multiple data schemas into a single large schema. Instead, composite APIs should translate specific tasks into the individual areas downstream that they affect.

For example, a user creating an account at the same time as submitting their first order would need to trigger multiple services: an order service to manage the inventory and kickoff a shipment, a payment processor service, and an account creation service. A composite service receives the request with all the information provided for the order and calls out to these services so they each perform their designated functions. The order service returns the order confirmation number, the payment processor the payment receipt information, and the account creation service confirms the account has been created. The composite service then takes these replies and creates a data model with just the information that needs to be returned to the customer.

Sizing and Scaling

Sharing and reuse of APIs mean that they need to be designed to allow for scaling up of capacity. An API which becomes more and more popular may find itself in a situation where performance starts to degrade as more and more systems start to consume it.

Initial Sizing

A baseline of performance metrics for any API should always be established. At minimum, an API shared with other systems or organizations should be load tested for the following parameters up to at least double (2x) of the anticipated peak usage over the first year:

The upper limit of when the API starts to fail at the currently allocated infrastructure capacity should also be established in terms of both throughput and concurrency.

This load testing should be done as a part of a whole system test which puts load on the entire system rather than just a single API to be representative of production scenarios. Baselining Technical and Utility APIs are less critical, but overall system load testing should still be conducted to ensure APIs are not creating performance bottlenecks.

Capacity Scaling

Well-constructed APIs should have relatively little code and not require significant compute capacity. As such, the constraining factor for most APIs are around concurrency (e.g., network connections, threads) rather than capacity (e.g., memory, CPU). Therefore horizontal scaling by adding more containers or VM’s is usually more effective than allocating more compute capacity. There are scenarios where the API is packaged as a part of a more integrated application (e.g., analytics platforms, COTS), in which case the API cannot be scaled independently of the application it exposes.

Throttling and Traffic Management

API Management and Gateway platforms should be deployed for APIs exposed to external or third party consumers. Any APIs shared across the GC or to consumers outside of GC should be using the GC API Store for this. These capabilities provide request throttling and traffic management features, which allows concurrency and throughput to be capped, defers the need to scale the API while removing the risk of API failure should demand exceed capacity. The caveat to this approach is that once the limits are hit, consumers will start receiving either failure or rejection messages even though the actual API remains fully operational. This should only be used to manage against exceptional load and not a replacement for effective scaling of the API.

Page details

Date modified: