The FireTail APISIX plugin provides realtime validation of your API traffic against an OpenAPI specification.
A demo setup using the FireTail APISIX plugin can be found in the /demo
directory.
Docker compose is used to run:
- An APISIX container which has been modified to use the FireTail APISIX plugin.
- An NGINX instance with mocked API responses which can be used to demonstrate request and response validation.
Getting it up and running should be as simple as:
git clone [email protected]:FireTail-io/firetail-apisix-go-plugin.git
cd firetail-apisix-go-plugin/demo
docker compose up
You can then make a request to the health endpoint to check everything's working:
curl localhost:9080/health
To get the demo setup to ship logs to the FireTail SaaS platform, create a copy of demo/.env.example
at demo/.env
and modify it.
cp .env.example .env
nano .env
To demonstrate request validation, an API endpoint POST /profile/{username}/comment
is defined in the demo's OpenAPI specification.
The following request conforms to the OpenAPI specification, so should yield a successful response:
curl -X POST localhost:9080/profile/alice/comment -H "Content-Type: application/json" -d '{"comment": "Hey, Alice!"}'
{"message":"Success!"}
If we change the content of the comment to be an integer, instead of a string, it will no longer conform to the OpenAPI specification and should yield an informative error response from the FireTail APISIX plugin:
curl -X POST localhost:9080/profile/alice/comment -H "Content-Type: application/json" -d '{"comment": 123456789}'
{"code":400,"title":"something's wrong with your request body","detail":"the request's body did not match your appspec: request body has an error: doesn't match the schema: Error at \"/comment\": field must be set to string or not be present\nSchema:\n {\n \"type\": \"string\"\n }\n\nValue:\n \"number, integer\"\n"}
To demonstrate response validation, an API endpoint GET /profile/{username}
is defined in the demo's OpenAPI specification, and two corresponding responses are defined in the nginx.conf
of the NGINX instance being used as a mock backend for this demo:
location /profile/alice {
return 200 '{"username":"alice", "friends": 123456789}';
}
location /profile/bob {
return 200 '{"username":"bob", "friends": 123456789, "address":"Oh dear, this shouldn\'t be public!"}';
}
Alice's profile conforms to the OpenAPI specification:
curl localhost:9080/profile/alice
{"username":"alice", "friends": 123456789}
Bob's profile does not conform to the OpenAPI specification. The response from the FireTail APISIX plugin will tell us why:
curl localhost:9080/profile/bob
{"code":500,"title":"internal server error","detail":"the response's body did not match your appspec: response body doesn't match the schema: property \"address\" is unsupported\nSchema:\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"friends\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"username\": {\n \"type\": \"string\"\n }\n },\n \"type\": \"object\"\n }\n\nValue:\n {\n \"address\": \"Oh dear, this shouldn't be public!\",\n \"friends\": 123456789,\n \"username\": \"bob\"\n }\n"}{"code":500,"title":"internal server error","detail":"the response's body did not match your appspec: response body doesn't match the schema: property \"address\" is unsupported\nSchema:\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"friends\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"username\": {\n \"type\": \"string\"\n }\n },\n \"type\": \"object\"\n }\n\nValue:\n {\n \"address\": \"Oh dear, this shouldn't be public!\",\n \"friends\": 123456789,\n \"username\": \"bob\"\n }\n"}
In the root directory, run make build
In the root directory, run the unix socket with APISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock APISIX_CONF_EXPIRE_TIME=3600 ./go-runner run
Then in apisix's configuration file config.yaml
add this:
ext-plugin:
path_for_test: /tmp/runner.sock
and restart apisix apisix restart
NOTE You will need to run your application at localhost (127.0.0.1) port 1980. If you wish to point it elsewhere, change the "nodes" parameter from example below.
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/profile/alice/comment",
"plugins": {
"ext-plugin-pre-req": {
"conf": [
{"name":"firetail", "value":"{\"body\":\"\"}"}
]
},
"ext-plugin-post-resp": {
"conf": [
{"name":"firetail", "value":"{\"body\":\"\"}"}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'