Apache APISIX Serverless Plugin for Event Hooks
Tags: webdev, programming, serverless, eventdriven
Published: 2023-02-21 14:16:55

[Apache APISIX](https://apisix.apache.org/) is an open-source, high-performance [API gateway](https://apisix.apache.org/docs/apisix/terminology/api-gateway/) built on top of [Nginx](https://www.nginx.com/). One of its powerful features is the ability to create serverless functions, which are small, stateless programs that can extend the functionality of Apache APISIX. In this article, we'll cover the basics of the Apache APISIX [serverless](https://apisix.apache.org/docs/apisix/plugins/serverless/) **plugin** and how it can be used to trigger **serverless functions** in response to events.

## Learning objectives

You will learn the following throughout the article:

- What is Apache APISIX serverless plugin?
- How the serverless plugin works and how to use it?
- Serverless plugin use-cases.
- How you can use serverless plugin to integrate with a [webhook](https://en.wikipedia.org/wiki/Webhook).

## What is Apache APISIX serverless plugin?

The Apache APISIX [serverless](https://apisix.apache.org/docs/apisix/plugins/serverless/) plugin for event hooks allows you to write serverless functions and integrate them into the API gateway. The plugin provides a simple and flexible way to run custom code in response to events, without having to manage the underlying infrastructure.

Serverless plugin separates the logic for handling events into separate serverless functions, you can simplify your API architecture and make it easier to manage. Apache APISIX provides support for serverless frameworks for popular cloud vendors such as [Azure Functions](https://azure.microsoft.com/en-in/services/functions/), [AWS Lambda](https://aws.amazon.com/lambda/). 

## How to use the serverless plugin?

To use the Apache APISIX serverless plugin for event hooks, you'll need to write a serverless function code in [Lua](https://www.lua.org/) programming language that implements the logic that you want to run in response to an event and enable `serverless-pre-function` or `serverless-post-function` plugin depending on the phases of the request-response cycle of your choice. 

> Currently, APISIX only supports Lua to write a function code. If you prefer other programming languages, you can always use [plugin runners](https://apisix.apache.org/docs/apisix/external-plugin/) to create a new [custom plugin](https://apisix.apache.org/docs/apisix/plugin-develop/) from scratch. 

## Serverless plugin use-cases

Here are a few use cases for the Apache APISIX serverless plugin for event hooks:

1. **Dynamic Routing**: The serverless plugin can be used to dynamically route incoming API requests based on specific criteria, such as the request method, URL path, or header values. This allows you to easily implement complex routing scenarios without having to modify the underlying Apache APISIX configuration.

2. **Authentication and Authorization**: You can use the serverless plugin to implement authentication and authorization checks for your API. For example, you can write a serverless function that verifies the presence of a valid API key in the request headers before allowing the request to continue. Or it can be used as an external authorization service with the combination of [forward-auth](https://apisix.apache.org/docs/apisix/plugins/forward-auth/) plugin.  

3. **Request Transformation**: The serverless plugin can be used to transform incoming API requests before they are processed by the backend service. For example, you can write a serverless function that modifies the request headers or body to match the format expected by the backend service.

4. **Response Transformation**: You can also use the serverless plugin to transform the response from the backend service before it is sent back to the client. For example, you can write a serverless function that modifies the response headers or body to match the format expected by the client.

5. **Logging and Monitoring**: You can use the serverless plugin to implement logging and monitoring for your API. For example, you can write a serverless function that logs detailed information about each API request, such as the request method, URL, headers, and body, for later analysis.

## Integrating Apache APISIX with webhooks (Demo)

To integrate Apache APISIX with webhooks, you need to create a serverless function that listens for incoming POST requests to the URL endpoint, checks if the request method is POST, and sends a webhook notification to the third-party service whenever a new message is posted. 

The serverless function should also return the response from the webhook, so you can see the status of the webhook and any error messages if something goes wrong.  In both cases, APISIX can communicate with the target service, letting the target know that an event was triggered by calling a provided URL with information about that event.

### Prerequisites

- [Docker](https://www.docker.com/products/docker-desktop/) installed on your machine to run APISIX.
- Basic knowledge about couple of APISIX core concepts such as [Route](https://apisix.apache.org/docs/apisix/terminology/route/), [Upstream ](https://apisix.apache.org/docs/apisix/terminology/upstream/)and [Plugin](https://apisix.apache.org/docs/apisix/terminology/plugin/).

### Set up the project

This first thing you clone the [apisix-docker](https://github.com/apache/apisix-docker) project repo from [GitHub](https://github.com/):

```bash
git clone https://github.com/apache/apisix-docker.git
```
Open the project folder in your favorite code editor. The tutorial leverages [VS Code](https://code.visualstudio.com/). 

### Install and run Apache APISIX

To run Apache APISIX, you can follow these steps:

Open a new terminal window and run `docker compose up` command from the root folder of the project:

```bash
docker compose up -d
```
Above command will run Apache APISIX and etcd together with Docker.

> We installed APISIX using Docker in this demo. However, there are other options to install it on [installation guide](https://apisix.apache.org/docs/apisix/installation-guide/).

### Configure the serverless function in Apache APISIX

To set up the serverless function in Apache APISIX, you need to create a new [route](https://apisix.apache.org/docs/apisix/terminology/route/) for the endpoint and configure the serverless plugin with our custom function code in Lua.

Here's an example of a serverless function in Lua that listens for incoming POST requests to the endpoint and sends a webhook notification:

```lua
function webhook(conf, ctx)

    -- Import neccessary libraries
    local http = require("resty.http")
    local core = require("apisix.core")

    -- Send the webhook notification only if the request method is POST, otherwise skip and send it to the upstream as usual
    if core.request.get_method() == "POST" then
        -- Send the webhook notification to the specified URL
        local httpc = http.new()
        local res, err = httpc:request_uri("http://webhook.site/9db3d3a0-ab64-4142-a39f-d4852ca50f8d", {
            method = "POST",
            headers = {
                ["Content-Type"] = "application/json"
            },
            body = core.request.get_body(),
        })
        -- Check the response from the webhook
        if not res then
            core.log.error("Failed to send webhook: ", err)
            return 500, err
        end
    end

    -- Return the response from the upstream service
    return conf.status, conf.body
end
```

As you can see in the above sample webhook function code, it makes JSON POST requests to a provided URL with the request body (the request body from the client to the API Gateway) as a payload. Testing webhooks website is used to mock our webhook endpoint: https://webhook.site.

To create and set your webhook endpoint:

1. Generate a URL by navigating to https://webhook.site in your web browser.
2. Select **Copy to clipboard** next to **Your unique URL**.
3. Paste it to the **request_uri** method of HTTP request in the function code.

Also, all other requests including POST will be forwarded further down to the upstream service. In the next step, we configure a route and upstream. 

### Configure a Route and Upstream

Now we will create a new route with the serverless function plugin enabled and an upstream that acts as our backend service.

```bash
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/1' \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
    -H 'Content-Type: application/json' \
    -d '{
    "uri": "/post",
    "plugins": {
        "serverless-pre-function": {
            "phase": "rewrite",
            "functions" : ["
                    return function(conf, ctx)
                        -- Import neccessary libraries
                        local http = require(\"resty.http\")
                        local core = require(\"apisix.core\")

                        -- Send the webhook notification only if the request method is POST, otherwise skip and send it to the upstream as usual
                        if core.request.get_method() == \"POST\" then
                            -- Send the webhook notification to the specified URL
                            local httpc = http.new()
                            local res, err = httpc:request_uri(\"http://webhook.site/9db3d3a0-ab64-4142-a39f-d4852ca50f8d\", {
                                method = \"POST\",
                                headers = {
                                    [\"Content-Type\"] = \"application/json\"
                                },
                                body = core.request.get_body(),
                            })
                            -- Check the response from the webhook
                            if not res then
                                core.log.error(\"Failed to send webhook: \", err)
                                return 500, err
                            end
                        end

                        -- Return the response from the upstream service
                        return conf.status, conf.body
                    end"]
        }
    },
    "upstream": {
        "nodes": {
            "httpbin.org:80": 1
        },
        "type": "roundrobin"
    }
}'
```

In the preceding route configuration example, we created our first route by sending `curl` request to APISIX [Admin API](https://apisix.apache.org/docs/apisix/admin-api/). In the Route request body, we specified that any requests to path `/post` will trigger the serverless function code and be forwarded later to the target `httpbin.org`. This means the request will be routed to `httpbin.org/post` endpoint after it checks if the request method is POST and the event should be sent to the 3rd party service webhook endpoint or not. The backend service can be replaced with your backend service. 

You may also notice that we added the function code into `functions` properties of `serverless-pre-function` in the Json object. 

### Test the configuration

Finally, we test by calling `/post ` API Gateway endpoint if everything works as we expected and the post event will be sent to the webhook website. 

```bash
curl -i http://127.0.0.1:9080/post -X POST -d \
'{
   "message": "A new webhook message"
}'
```

After, you navigate to the webhook URL from the https://webhook.site page that you generated in the previous steps. You should see a POST request, notifying our webhook endpoint about the event and sent the request body.

![APISIX payload send to the webhook](https://static.apiseven.com/uploads/2023/02/12/Hy50dY6l_5abc9b3e4ow6o1j8ggp4.png)

In addition to this, we get the response back from the mock upstream service `httpbin.org`:

```bash
HTTP/1.1 200 OK
Content-Type: application/json

{
  ...
  "form": {
    "{\n   \"message\": \"A new webhook message\"\n}": ""
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "41",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "127.0.0.1",
    "X-Forwarded-Host": "127.0.0.1"
  },
  "json": null,
  "url": "http://127.0.0.1/post"
}
```

Obviously, if you send other methods of HTTP requests apart from POST, the serverless function code will not trigger the webhook endpoint.

This is a basic example of setting up a serverless function in Apache APISIX. You can extend this functionality by adding more complex logic to your serverless function such as triggering another function in response to webhooks.

## Conclusion

The Apache APISIX serverless plugin for event hooks provides a flexible and scalable way to trigger serverless functions in response to events that occur during the processing of API requests. By using the plugin, you can simplify your API architecture, improve the performance and reliability of your services, and reduce the cost of managing your application infrastructure. 

### Related resources

- [Event-Driven APIs with Webhook and API Gateway](https://apisix.apache.org/blog/2022/11/07/webhook-api-gateway-event-driven-apis/)

### Recommended content

- [Manage serverless APIs with Apache APISIX](https://www.iambobur.com/post/manage-serverless-apis-with-apache-apisix)

- [How to choose the right API Gateway](https://iambobur.com/2022/11/22/how-to-choose-the-right-api-gateway/)

- [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)

### About the author

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