Develop Custom Lua Plugins
Custom Lua plugins allow you to extend the functionality of API7 Gateway by executing custom logic during different phases of the request/response lifecycle. This guide walks you through creating a simple Lua plugin, registering it in API7 Gateway, and applying it to a route.
Prerequisites
- Basic knowledge of the Lua programming language.
- Familiarity with OpenResty and the Nginx request lifecycle.
- Access to an API7 Gateway instance and its Dashboard.
Plugin Structure
A Lua plugin is a module that exports a table containing its configuration schema, priority, and phase handlers.
Standard Fields
Every plugin should define the following fields:
name: The unique name of the plugin.version: The version of the plugin.priority: Determines the execution order. Plugins with higher priority run earlier.schema: A JSON schema that defines the configuration parameters for the plugin.
Execution Phases
API7 Gateway executes plugins in the following order:
rewrite: Modify the request before routing (e.g., changing the URI).access: Main processing phase (e.g., authentication, rate limiting).before_proxy: After the access phase, just before the request is proxied to the upstream.header_filter: Modify response headers.body_filter: Modify the response body.log: Record logs after the response has been sent to the client.
Step-by-Step Development
1. Create the Plugin File
Create a new Lua file for your plugin. For this example, we will create a plugin named custom-header.
2. Define the Schema and Priority
Use apisix.core to validate the plugin configuration against a JSON schema.
local core = require("apisix.core")
local schema = {
type = "object",
properties = {
header_name = {
type = "string",
default = "X-Custom-Lua"
},
header_value = {
type = "string",
default = "loaded"
}
}
}
local plugin_name = "custom-header"
local _M = {
version = 0.1,
priority = 1500,
name = plugin_name,
schema = schema,
}
function _M.check_schema(conf)
return core.schema.check(schema, conf)
end
3. Implement Phase Handlers
Implement the logic in the appropriate phase. To add a header to the request before it reaches the upstream, use the access phase.
function _M.access(conf, ctx)
core.request.set_header(ctx, conf.header_name, conf.header_value)
end
4. Complete Plugin Example
Here is the full code for the custom-header plugin:
local core = require("apisix.core")
local schema = {
type = "object",
properties = {
header_name = {
type = "string",
default = "X-Custom-Lua"
},
header_value = {
type = "string",
default = "loaded"
}
}
}
local plugin_name = "custom-header"
local _M = {
version = 0.1,
priority = 1500,
name = plugin_name,
schema = schema,
}
function _M.check_schema(conf)
return core.schema.check(schema, conf)
end
function _M.access(conf, ctx)
core.request.set_header(ctx, conf.header_name, conf.header_value)
end
return _M
Register the Plugin in API7 Gateway
In API7 Gateway, custom plugins are managed from the control plane. After you prepare the Lua source file, upload and deploy it from the Dashboard so it becomes available in the plugin picker for routes, services, consumers, or global rules.
1. Add the Plugin in the Dashboard
In the Dashboard, go to Gateway Settings and open the Custom Plugin tab, then click Add Custom Plugin.
Upload the Lua source file and provide the required plugin metadata:
- Plugin Source Code File: upload
custom-header.lua - Plugin Description: briefly describe what the plugin does
- Plugin Documentation Link: optionally link to your plugin documentation
- Plugin Author: identify the owner of the plugin
- Deployed Gateway Group: select the gateway groups where the plugin should be available
When the file is uploaded successfully, API7 Gateway parses the plugin and displays its detected name and version in the dialog before you add it.
After the plugin is added, it can be selected from the Add Plugin dialog on routes and services in the deployed gateway groups.
For the product workflow, see Custom Plugins and Sandbox.
2. Confirm the Plugin is Available
Open a route or service in one of the deployed gateway groups and confirm that custom-header appears in the Add Plugin dialog.
Test the Plugin
Once the plugin source code is available on the gateway node and the plugin is registered in API7 Gateway, you can enable it on a specific route using the Admin API or ADC.
- Create a route with the plugin enabled:
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/services/custom-header-service?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "custom-header-service",
"upstream": {
"type": "roundrobin",
"scheme": "http",
"nodes": [
{
"host": "httpbin.org",
"port": 80,
"weight": 100
}
]
}
}'
curl -k "https://localhost:7443/apisix/admin/routes/custom-header-route?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "custom-header-route",
"methods": ["GET"],
"paths": ["/get"],
"service_id": "custom-header-service",
"plugins": {
"custom-header": {
"header_name": "X-Trace-ID",
"header_value": "docs-test"
}
}
}'
services:
- name: custom-header-service
upstream:
nodes:
- host: httpbin.org
port: 80
weight: 1
routes:
- name: custom-header-route
uris:
- /get
plugins:
custom-header:
header_name: X-Trace-ID
header_value: docs-test
adc sync -f adc.yaml
- Send a request and verify the header on the upstream response body:
curl -i "http://localhost:9080/get"
The httpbin response should include X-Trace-ID: docs-test under headers, which confirms the plugin modified the request before proxying.
If the proxy returns 404 Route Not Found immediately after the route is created, wait a few seconds and retry the request.
Debugging Tips
- Check Error Logs: By default, logs are located at
/usr/local/apisix/logs/error.log. - Syntax Errors: Use
luac -p custom-header.luato check for Lua syntax errors before uploading. - Plugin Visibility: If the plugin does not appear in the Dashboard plugin list, verify it was deployed to the correct gateway group.
- Admin API Payload Shape: In API7 Gateway, route creation uses
pathsandservice_idrather than APISIX-style top-leveluriand inlineupstream. - Name Conflicts: Ensure the plugin name is unique and does not collide with a built-in plugin.
Next Steps
- Plugins — understand plugin execution order and scopes.