Consume external services in a custom plugin
Use the lua-resty-http and lua-cjson libraries to make HTTP requests and parse the JSON responses.
Prerequisites
Series Prerequisites
This page is part of the Get started with custom plugin development series.
Complete the previous page, Add a custom plugin configuration before completing this page.
Include HTTP and JSON support
Start by importing two new libraries to the handler.lua file to enable
HTTP and JSON parsing support.
We’ll use the lua-resty-http library for HTTP client connectivity to the third-party service.
For JSON support, we’ll use the lua-cjson library.
Add the libraries to the top of handler.lua:
local http = require("resty.http")
local cjson = require("cjson.safe")
Send third-party HTTP requests
The lua-resty-http library provides a simple HTTP request
function (request_uri) that we can use to reach out to our third-party service.
In this example, we’ll send a GET request to the httpbin.org/anything API.
Add the following to the top of the MyPluginHandler:response function inside the
handler.lua module:
local httpc = http.new()
local res, err = httpc:request_uri("http://httpbin.konghq.com/anything", {
method = "GET",
})
If the request to the third-party service is successful, the res
variable will contain the response.
Handle response errors
The Kong Gateway Plugin Development Kit provides you with various functions to help you handle error conditions.
In this example, we’re processing responses from the upstream service and decorating the client response with values from the third-party service. If the request to the third-party service fails, we can terminate the response processing and return to the client with an error, or continue processing the response and not complete the custom header logic.
In this example, we’ll terminate the
response processing and return a 500 internal server error to the client.
Add the following to the MyPluginHandler:response function, immediately
after the httpc:request_uri call:
if err then
return kong.response.error(500,
"Error when trying to access third-party service: " .. err,
{ ["Content-Type"] = "text/html" })
end
After this step, the handler.lua file looks like this:
local http = require("resty.http")
local cjson = require("cjson.safe")
local MyPluginHandler = {
PRIORITY = 1000,
VERSION = "0.0.1",
}
function MyPluginHandler:response(conf)
local httpc = http.new()
local res, err = httpc:request_uri("http://httpbin.konghq.com/anything", {
method = "GET",
})
if err then
return kong.response.error(500,
"Error when trying to access third-party service: " .. err,
{ ["Content-Type"] = "text/html" })
end
kong.response.set_header(conf.response_header_name, "response")
end
Process JSON data from third-party response
This third-party service returns a JSON object in the response body. We’ll parse and extract a single value from the JSON body.
- In your
handler.luafile, use thedecodefunction in thelua-cjsonlibrary passing in theres.bodyvalue received from therequest_urifunction. Add this to your file right below what you added in the previous step:local body_table, err = cjson.decode(res.body)Copied!The
decodefunction returns a tuple of values. The first value contains the result of a successful decoding and represents the JSON as a table containing the parsed data. If an error occurs, the second value will contain error information (ornilon success). - Add the following to the
MyPluginHandler:responsefunction after the previous line to stop processing in case of an error:if err then return kong.response.error(500, "Error while decoding third-party service response: " .. err, { ["Content-Type"] = "text/html" }) endCopied! - Update the following line after the error handling to set the value of the
urlfield in the response as the header value instead ofresponse:kong.response.set_header(conf.response_header_name, body_table.url)Copied!
After this step, the handler.lua file looks like this:
local http = require("resty.http")
local cjson = require("cjson.safe")
local MyPluginHandler = {
PRIORITY = 1000,
VERSION = "0.0.1",
}
function MyPluginHandler:response(conf)
kong.log("response handler")
local httpc = http.new()
local res, err = httpc:request_uri("http://httpbin.konghq.com/anything", {
method = "GET",
})
if err then
return kong.response.error(500,
"Error when trying to access third-party service: " .. err,
{ ["Content-Type"] = "text/html" })
end
local body_table, err = cjson.decode(res.body)
if err then
return kong.response.error(500,
"Error when decoding third-party service response: " .. err,
{ ["Content-Type"] = "text/html" })
end
kong.response.set_header(conf.response_header_name, body_table.url)
end
return MyPluginHandler
Update the tests
At this stage, using the pongo run command to execute the integration tests will result in errors.
The value of the header has changed from response to http://httpbin.konghq.com/anything.
- Update the expected header value in
spec/my-plugin/01-integration_spec.lua:-- validate the value of that header assert.equal("http://httpbin.konghq.com/anything", header_value)Copied! - Run the tests:
pongo runCopied!Pongo should report a successful test run.
Note: This series provides examples to get you started. In a real plugin development scenario, you would want to build integration tests for third-party services by providing a test dependency using a mock service instead of making network calls to the actual third-party service used. Pongo supports test dependencies for this purpose. See the Pongo documentation for details on setting test dependencies.