Chaining API requests with API Gateway
Tags: webdev, api, gateway, rest
Published: 2023-05-24 08:31:23

As the number of APIs that need to be integrated increases, managing the complexity of API interactions becomes increasingly challenging. By using the [API Gateway](https://apisix.apache.org/docs/apisix/terminology/api-gateway/) we can create a sequence of API calls, which breaks down the API workflows into smaller, more manageable steps. For example, in an online shopping website when a customer searches for a product, the platform can send a request to the product search API, then send a request to the product details API to retrieve more information about the products. In this article, we will **create a custom plugin** for [Apache APISIX](https://apisix.apache.org/) API Gateway to handle client requests that should be called in sequence.

## Learning objectives

You will learn the following throughout the article:

- What are chaining API requests?
- Example of sequential API calls.
- How to build a custom pipeline-request plugin for Apache APISIX.
- Pipeline-request plugin demo.

![Pipelining API requests with API Gateway](https://static.apiseven.com/uploads/2023/05/22/vpzBzkxK_Pipelining%20API%20requests%20with%20API%20Gateway%20%2812%29.png)

## What is a chaininng API request and why do we need it?

Chaining API requests (or pipeline requests, or sequential API calls) is a technique used in software development to manage the complexity of API interactions where software **requires multiple API calls to complete a task**. It is similar to [batch request processing](https://api7.ai/blog/batch-request-processing-with-api-gateway) where you group multiple API requests into a single request and send them to the server as a batch. While they may seem similar, a pipeline request involves sending a single request to the server that triggers a sequence of API requests to **be executed in a defined order**. Each API request in the sequence can modify the request and response data, and the response from one API request is passed as input to the next API request in the sequence. Pipeline requests can be useful when a client needs to execute a sequence of dependent API requests that must be executed in a specific order.

In each step of the pipeline, we can **transform or manipulate** the response data before passing it on to the next step. This can be useful in situations where data needs to be **normalized or transformed** or **filter out sensitive data** before it is returned to the client. It can help to **reduce overall latency**. For example, one API call can be made while another is waiting for a response, reducing the overall time needed to complete the workflow.

![sequential API calls with API Gateway](https://static.apiseven.com/uploads/2023/05/22/TnnnefMY_Pipelining%20API%20requests%20with%20API%20Gateway%20%2814%29.png)

## Custom pipeline-request plugin for Apache APISIX

An API gateway can be the right place to implement this functionality because it can intercept all client app requests and forward them to intended destinations. We are going to use [Apache APISIX](https://apisix.apache.org/) as it is a popular open-source API Gateway solution with a bunch of [built-in plugins](https://docs.api7.ai/apisix/key-concepts/plugins). However, at the time of developing the current blog post, APISIX did not have official support for the pipeline-request plugin. By having the knowledge of [custom plugin development](https://apisix.apache.org/docs/apisix/plugin-develop/) capabilities of APISIX, we decided to introduce a new plugin that can offer the same feature. There is a repo on GitHub with [source code](https://github.com/bzp2010/apisix-plugin-pipeline-request/blob/main/apisix/plugins/pipeline-request.lua) written in [Lua programming language](https://www.lua.org/) and a description of the [pipeline-request plugin](https://github.com/bzp2010/apisix-plugin-pipeline-request).

With this plugin, you can specify a list of upstream APIs that should be called in sequence to handle a single client request. Each upstream API can modify the request and response data, and the response from one upstream API is passed as the input to the next upstream API in the pipeline. The pipeline can be defined in a [Route](https://docs.api7.ai/apisix/key-concepts/routes) configuration, and you can also define orders for API URLs when the pipeline should execute them.

Let’s understand the usage of this plugin with an example. Suppose you have two APIs - one that makes GET `/credit_cards` requests to retrieve credit card information and another that receives previous response data in the body of POST `/filter` request and then it filters out the sensitive data (such as the credit card number and expiration date) before returning the response to the client. Because the credit card API endpoint returns sensitive information that should not be exposed to unauthorized parties. The below diagram illustrates the overall data flow:

![sequential API calls with API Gateway to filter](https://static.apiseven.com/uploads/2023/05/22/VPyZJ6Zn_Pipelining%20API%20requests%20with%20API%20Gateway%20%2820%29.png)

1. When a client makes a request to the credit card API endpoint of API Gateway to retrieve all credit card info, API Gateway forwards a request to retrieve the credit card data from the credit card backend service.
2. If the request is successful and returns credit card data, then it passes to the next step in the pipeline which security service
3. When the filtered response is received from the security service, it returns the combined response to the client.

## Pipeline-request plugin demo

For this demo, we are going to leverage another prepared [demo project](https://github.com/Boburmirzo/apisix-plugin-pipeline-request-demo) on GitHub where you can find all [curl command examples](https://github.com/Boburmirzo/apisix-plugin-pipeline-request-demo/tree/main/curl-cmds) used in this tutorial, run APISIX and enable a [custom plugin](https://github.com/Boburmirzo/apisix-plugin-pipeline-request-demo/blob/main/custom-plugins/pipeline-request.lua) without additional configuration with a [Docker compose](https://github.com/Boburmirzo/apisix-plugin-pipeline-request-demo/blob/main/docker-compose.yml).yml file.

### Prerequisites

- [Docker](https://docs.docker.com/get-docker/) is used to installing the containerized etcd and APISIX.
- [curl](https://curl.se/) is used to send requests to APISIX Admin API. You can also use easy tools such as [Postman](https://www.postman.com/) to interact with the API.

### Step 1: Install and run APISIX and etcd

You can easily install APISIX and etcd by running `docker compose up` from the project root folder after you fork/clone [the project](https://github.com/Boburmirzo/apisix-plugin-pipeline-request-demo). You may notice that there is a volume `./custom-plugins:/opt/apisix/plugins:ro` specified in `docker-compose.yml` file. This mounts the local directory**`./custom-plugins`** where our `pipeline-request.lua` file with the custom plugin implementation as a read-only volume in the docker container at the path **`/opt/apisix/plugins`**. This allows custom plugins to be added to APISIX in the runtime (This setup is only applicable if you run APISIX with docker).

### Step 2:  Create the first Route with the pipeline-request plugin

Once APISIX is running, we use cURL command that is used to send an HTTP PUT request to the APISIX Admin API `/routes` endpoint to create our first route that listens for URI path `/my-credit-cards`.

```bash
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/1' \
--header 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
--header 'Content-Type: application/json' \
--data-raw '{
   "uri":"/my-credit-cards",
   "plugins":{
      "pipeline-request":{
         "nodes":[
            {
               "url":"https://random-data-api.com/api/v2/credit_cards"
            },
            {
               "url":"http://127.0.0.1:9080/filter"
            }
         ]
      }
   }
}'
```

The important part of the configuration is the "plugins" section, which specifies that the "pipeline-request" plugin should be used for this API route. The plugin configuration contains a "nodes" array, which defines the sequence of API requests that should be executed in the pipeline. You can define one or multiple APIs there. In this case, the pipeline consists of two nodes: the first node sends a request to the [https://random-data-api.com/api/v2/credit_cards](https://random-data-api.com/api/v2/credit_cards)  ****API to retrieve credit card data, and the second node sends a request to a local API at [http://127.0.0.1:9080/filter](http://127.0.0.1:9080/filter) to filter out sensitive data from the credit card information. The second API will be just a serverless function using the [serverless-pre-function](https://apisix.apache.org/docs/apisix/plugins/serverless/) APISIX plugin.  It acts just as a backend service to modify the response from the first API.

### Step 3:  Create the second Route with the serverless plugin

Next, we configure a new route with the ID 2 that handles requests to `/filter` endpoint in the pipeline. It also enables [serverless-pre-function](https://apisix.apache.org/docs/apisix/plugins/serverless/) APISIX’s existing plugin where we specify a Lua function that should be executed by the plugin. This function simply retrieves the request body from the previous response, replaces the credit card number field, and leaves the rest of the response unchanged. Finally, it sets the current response body to the modified request body and sends an HTTP 200 response back to the client. You can modify this script to suit your needs, such as by using the decoded body to perform further processing or validation.

```bash
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/2' \
--header 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
--header 'Content-Type: application/json' \
--data-raw '
{
  "uri": "/filter",
  "plugins":{
    "serverless-pre-function": {
            "phase": "access",
            "functions": [
                "return function(conf, ctx)
                    local core = require(\"apisix.core\")
                    local cjson = require(\"cjson.safe\")

                    -- Get the request body
                    local body = core.request.get_body()
                    -- Decode the JSON body
                    local decoded_body = cjson.decode(body)

                    -- Hide the credit card number
                    decoded_body.credit_card_number = \"****-****-****-****\"
                    core.response.exit(200, decoded_body);
                end"
            ]
        }
    }
}'
```

### Step 3: Test setup

Now it is time to test the overall config. With the below curl command, we send an HTTP GET request to the endpoint **`http://127.0.0.1:9080/my-credit-cards`**.

```bash
curl http://127.0.0.1:9080/my-credit-cards
```

We have the corresponding route configured in the second step to use the **`pipeline-request`** plugin with two nodes, this request will trigger the pipeline to retrieve credit card information from the **`https://random-data-api.com/api/v2/credit_cards`** endpoint, filter out sensitive data using the **`http://127.0.0.1:9080/filter`** endpoint, and return the modified response to the client. See the output:

```json
{
   "uid":"a66239cd-960b-4e14-8d3c-a8940cedd907",
   "credit_card_expiry_date":"2025-05-10",
   "credit_card_type":"visa",
   "credit_card_number":"****-****-****-****",
   "id":2248
}
```

As you can see, it replaces the credit card number in the request body (Actually, it is the response from the first API call in the chain) with asterisks.

## Summary

Up to now, we learned that our custom pipeline request plugin for the Apache APISIX API Gateway allows us to define a sequence of API calls as a pipeline in a specific order. We can also use this new plugin with the combination of existing ones to enable authentication, security, and other API Gateway features for our API endpoints.

### Related resources

- [Batch request processing with API Gateway](https://api7.ai/blog/batch-request-processing-with-api-gateway)
- [Custom plugin development with APISIX](https://apisix.apache.org/docs/apisix/plugin-develop/)

### Recommended content

- [Dynamic routing based on user credentials with API Gateway](https://api7.ai/blog/dynamic-routing-based-on-user-credentials)
- [Why Is Apache APISIX the Best API Gateway?](https://api7.ai/blog/why-is-apache-apisix-the-best-api-gateway)

### Community

🙋 [Join the Apache APISIX Community](https://apisix.apache.org/docs/general/join/)
🐦 [Follow us on Twitter](https://twitter.com/ApacheAPISIX)
📝 [Find us on Slack](https://join.slack.com/t/the-asf/shared_invite/zt-vlfbf7ch-HkbNHiU_uDlcH_RvaHv9gQ)
💁 [How to contribute page](https://apisix.apache.org/docs/general/how-to-contribute/)

### About the author

Visit my blog: [www.iambobur.com](https://www.iambobur.com/)