Apache APISIX Serverless Plugin for Event Hooks

Bobur Umurzokov - Feb 21 '23 - - Dev Community

Apache APISIX is an open-source, high-performance API gateway built on top of Nginx. 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 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.

What is Apache APISIX serverless plugin?

The Apache APISIX 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, AWS 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 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 to create a new custom plugin 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 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 installed on your machine to run APISIX.
  • Basic knowledge about couple of APISIX core concepts such as Route, Upstream and Plugin.

Set up the project

This first thing you clone the apisix-docker project repo from GitHub:

git clone https://github.com/apache/apisix-docker.git
Enter fullscreen mode Exit fullscreen mode

Open the project folder in your favorite code editor. The tutorial leverages VS Code.

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:

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

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.

Configure the serverless function in Apache APISIX

To set up the serverless function in Apache APISIX, you need to create a new 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:

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
Enter fullscreen mode Exit fullscreen mode

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.

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"
    }
}'
Enter fullscreen mode Exit fullscreen mode

In the preceding route configuration example, we created our first route by sending curl request to 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.

curl -i http://127.0.0.1:9080/post -X POST -d \
'{
   "message": "A new webhook message"
}'
Enter fullscreen mode Exit fullscreen mode

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

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

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"
}
Enter fullscreen mode Exit fullscreen mode

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

Recommended content

Community

🙋 Join the Apache APISIX Community
🐦 Follow us on Twitter
📝 Find us on Slack

About the author

Visit my personal blog: www.iambobur.com

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .