How-to-Link-API-Requests-Effectively

How to Link API Requests Effectively

Posted in

The need to link API requests has been around for as long as there have been APIs. APIs enable a composable software architecture that naturally promotes the distribution of systems and the operations they support. “Functions” of an organization or business tend to become an aggregation of several API calls to distributed systems.

There is an argument that if you are implementing REST, then linking API requests should be unnecessary, especially given the role that hypermedia should play in providing context between requests. However, the interconnectedness of systems and — rightly or wrongly — the general mixing of architectural styles across platforms makes it increasingly difficult to rely on a single, well-scoped API call to accomplish something. Knitting together API requests from one or more API providers is critical to delivering functioning software systems.

There is, however, an important point to be made. We should consider that linking API requests is a design-time activity. Countless platforms allow implementers to orchestrate a sequence of API requests, but far fewer publish the information needed to make those platforms work programmatically. When API providers publish operations, they should consider how different operations relate to each other and make this information available to API consumers. API providers can obviously do this in API portals and documentation. However, in a world of automation and an increasing AI footprint, there is a use case for providing machine-readable documents.

In this post, we look at a couple of ways that API providers can provide descriptions of how API requests are linked together. We look at standards from the early days of the API economy to give some context to the problem space. We then discuss two approaches to providing design-time linking through OpenAPI Initiative (OAI) specifications.

Integration Architectures and Linking API Requests

The goal of linking requests has already been tackled repeatedly in integration architectures. As software and systems architectures have become distributed and tasks have moved off monolithic computing systems, there has been an increasing need to stitch activities together in a sensible way. The solutions have tended to follow the salient integration architectures of the day.

One example is Business Process Execution Language (BPEL), which is associated with web services and software-oriented architecture (SOA). BPEL is considered an orchestration language as it describes a sequence of activities controlled by a software component rather than — as choreography is described — a series of events with which software components interact.

BPEL is notable in that it provides a machine-readable description of a sequence of activities. Machine-readable descriptions are important in the API world when we consider them in the context of API specification languages, which are a critical part of providing information about a given API.

From the perspective of the API economy, however, BPEL does not provide a means to link API requests effectively for an API consumer. This is because the process is executed server-side, with a workflow server orchestrating activities and a document provided in Web Services Description Language (WSDL) notation describing the operation parameters. An API consumer cannot orchestrate the activity themselves, which will routinely be suboptimal for their use cases.

For this reason (and a number of others we will not go into here), BPEL and other similar standards from SOA have not been transposed into the API economy. API providers need other means to describe and implement linking between API requests that do not divest control for the API consumer. One example of how to accomplish this at design-time is to use Links in an OpenAPI document.

Links — specifically the Link Object — is a means to link one request to another when designing an API and providing a description document to API consumers. The specification describes a Link object as a means to describe “…a known relationship and traversal mechanism between responses and other operations.” This is helpful to API consumers as they are given knowledge of the relationship between endpoints that are consumed programatically, letting tooling that understands the Link object mediate the flow of data between API calls. The specification makes use of the Runtime Expression (which we mentioned in our recent AsyncAPI article) to describe how to access information from API responses to make a subsequent API request.

Here is a very simple example. The following is an excerpt of an OpenAPI document that describes two operations for a hypothetical foreign exchange API: Getting a quote, and then executing a trade based on the quoted price.

paths:
  /quotes:
    post:
      summary: Create a new quote for a given currency pair
      operationId: CreateQuote
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - tradeType
                - sellCurrency
                - buyCurrency
                - amount
              properties:
                tradeType:
                  type: string
                sellCurrency:
                  type: string
                buyCurrency:
                  type: string
                amount:
                  type: number
      responses:
        201:
          description: Created
          content:
            application/json:
              schema:
                type: object
                required:
                  - quoteId
                properties:
                  quoteId:
                    type: string
            # ... other quote properties
          links:
            executeTradeBasedOnQuote:
              operationId: CreateTrade
              requestBody: $.response.body#quoteId
  /trades:
    post:
      summary: Execute a trade based on a previous quote using quoteId
      operationId: CreateTrade
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - quoteId
              properties:
                quoteId:
                  type: string

The Operation labeled CreateQuote indicates to API consumers that they can execute a trade using the operation CreateTrade based on the response. The client must retrieve the value of quoteId from the response and use this to populate the request body.

This example shows how API providers can describe information about links between requests using OpenAPI which, when implemented using an operationRef property, can bridge between different OpenAPI documents. Providing this information is a very powerful feature as it removes the guesswork for API consumers who can leverage tools to automate the linking of requests.

However, there are times when it makes sense to describe linking across APIs from different providers. This is where the OAI Workflows can help.

Linking Across APIs: OpenAPI Workflows

As we’ve already said, linking across APIs is a common goal in an environment where platforms are the sum of their parts and integrate many different systems to provide a given function. Software developers often have to figure out how to make API calls in the correct sequence, carrying the right context between calls.

While this is acceptable for simple APIs, larger, more complex sequences are more demanding and can result in complex support requirements for API providers. This is where the OAI Workflows Special Interest Group (SIG) has attempted to fill the gap with the Workflows specification. The idea of this specification is to provide a description document that can be understood by both humans and machines, allowing them to seamlessly knit a sequence of events together.

A given Workflow document is intended to be implemented however the API consumer chooses — unlike the BPEL example — using their own choice of compute and software. This gives the API consumer a considerable “leg-up” in their implementation without compromising their control over how they orchestrate multiple API requests.

Taking the example from the specification — a workflow based on logging into the Petstore API — a workflow provider first describes and uniquely identifies the workflow. In this definition, they specify external inputs to the workflow, in this case username and password:

workflows:
- workflowId: loginUserAndRetrievePet
  summary: Login User and then retrieve pets
  description: This workflow lays out the steps to login a user and then retrieve pets
  inputs:
      type: object
      properties:
          username:
              type: string
          password:
              type: string

The workflow provider then outlines the steps in the workflow — in the example, the loginStep followed by the getPetStep — using Runtime Expressions to provide the reference to the input values and values to be reused between steps. For example, the input username is referenced using the expression $inputs.username:

  steps:
  - stepId: loginStep
    description: This step demonstrates the user login step
    operationId: loginUser
    parameters:
      # parameters to inject into the loginUser operation (parameter name must be resolvable at the referenced operation and the value is determined using {expression} syntax)
      - name: username
        in: query
        value: $inputs.username
      - name: password
        in: query
        value: $inputs.password
    successCriteria:
      # assertions to determine step was successful
      - condition: $statusCode == 200
    outputs:
      # outputs from this step
      tokenExpires: $response.header.X-Expires-After
      rateLimit: $response.header.X-Rate-Limit
      sessionToken: $response.body
  - stepId: getPetStep
    description: retrieve a pet by status from the GET pets endpoint
    operationRef: https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml#/paths/~1pet~1findByStatus/get
    dependsOn: loginStep
    parameters:
      - name: status
        in: query
        value: 'available'
      - name: Authorization
        in: header
        value: $steps.loginUser.outputs.sessionToken
    successCriteria:
      - condition: $statusCode == 200
    outputs:
      # outputs from this step
      availablePets: $response.body
  outputs:
      available: $steps.getPetStep.availablePets

The steps also provide success criteria, allowing workflow consumers to understand what to watch for in their code, dependencies, and the outputs from each step that can be consumed later.

The benefits of the Workflow specification are immediately apparent when you consider a Workflow document that helps API consumers implement integrations with multiple APIs. There is also the potential, however, for publishers in an API marketplace to leverage Workflow documents as well, knitting together sequences of API calls across multiple, separate APIs from different providers. The Workflow use case, while not necessarily new, therefore has great potential for simplifying the consumption of APIs for API consumers while still allowing them to control how they consume APIs.

For a more detailed look at OAI Workflows please see this post over at the Swagger website.

Final Thoughts: Adopting Standards for Linking API Requests

The examples above are one small part of the tools a developer can use to link between API requests. In this article, we’ve focused on linking at design-time and providing sequencing information through specification documents. Developers will, however, likely use other tools based on their programming language of choice or the platforms provided by the organization they work for.

Links in OpenAPI and OAI Workflows are not, however, just documents to be read by humans or their chosen AI. These specifications are machine-readable by design but are only really effective if tools support them. It’s in the gift of tooling makers and their communities to make these standards for linking API requests successful by implementing support for them.