Building a geocoding plugin for the Kong Gateway using Python
Recently, I came across a request from one of our community users looking for a Kong plugin that could look up address details based on a few location search keywords. There are many libraries that support geocoding. I decided to pick up a Python Library as they are simple to use — and the Kong Gateway supports plugins written in Python since version 2.3.
To show the simplicity of using the Kong Python PDK, I’ve picked the GeoPy library, which will allow us to perform GeoCoding within our Kong Plugin.
When making an API request to Kong, the caller should be able to pass location search keywords such as "Trafalgar Square" in the request header. The plugin will use the GeoPy library to lookup the address for the search terms, determine the latitude and longitude for the address, and add them as additional headers to the request before passing it to the upstream.
Bootstrapping your development environment
The Python plugin support in Kong Gateway works by running a Python plugin server as a separate process on the same host as Kong Gateway and passing messages back and forth using Unix Sockets. The same mechanism is used for other external plugins in Kong, such as JS and Go.
In order to make things easier, I've put together a Docker-based environment for you to use. All you require is a machine with Docker and Docker Compose installed. Since it might take a minute or two to download the images and build our environment, I recommend running it now in the background as you keep reading:
The Docker Compose file sets up the following:
Creates a custom Kong image from the base image, installing the Kong Python PDK as well as the GeoPy Python library
Starts up a Kong Gateway instance in the DB-less mode — the plugin will run with the DB-backed mode as well, but I wanted to use DBless to keep our sample lean
Mounts the Kong declarative config and Python plugin file to the Kong instance using Docker volumes
Exploring the plugin
The Kong Gateway is configured to start a Python plugin server and load all the plugins in the kong-py-plugin directory. Currently, it contains a single plugin file: py-geocode.py. We start our plugin by importing the required Python libraries:
These Python libraries should be installed using pip in our Dockerfile used to build our Kong image.
Like all well-written plugins, the py-geocode plugin exposes all its parameters via configuration 😎
The request header where the location search term will be specified
The upstream header which will contain the completed address
The upstream headers with the location latitude and longitude
We also specify the version and priority of the plugin. We start by implementing the plugin object and the phases where we want to implement our custom logic.
There are five phases available for HTTP requests in the life-cycle of a Kong Gateway request:
certificate - Executed once per request when the connection is SSL/TLS enabled for the proxy
rewrite - Performed before the API gateway does any routing of the request to the upstream
access - All routing is identified, and the plugin knows which service the request is bound to. This is the last phase before the API gateway sends the request to the upstream
response - Allows you to manipulate the response from the upstream. Implementing this phase has a performance penalty as it enables request buffering
log - Executed after the request has been completed
Since we want to add headers to the request before sending it to the upstream, we do it in the access phase:
We'll also be setting default values for these various headers if they're not set in plugin configuration. After all, we want our plugin to be resilient 💪
Here's a snippet:
Finally, we perform our custom logic that:
Extracts the location search keywords from the request header
Uses the GeoPy library to get the location details
Adds the upstream headers for the location address, latitude and longitude
We wrap all the logic in a try-except block for proper exception handling. This ensures that our plugin will not crash and burn when it hits an error 👊
Enabling the Plugin
The environment we're running uses Kong's declarative configuration capability. This means our Kong configuration is defined as a YAML file. Open up config/kong.yml, and you should see a service defined that proxies to httpbin.org/anything, and exposes a single route on the path "/any" that has our geocoding plugin applied:
The Kong Gateway only allows you to use plugins that are on an allowlist for security purposes. Open up docker-compose.yml and you can see that our plugin "py-geocode" has been enabled:
Finally, we enable the python plugin server using environment variables used to start the gateway container.
Trying it out
At this point the API gateway is ready to run our Python plugin, so let's go ahead and start it:
Once the containers are up, the Kong Gateway's proxy port will be available on port 8000 on our local machine.
Lets try a simple location search, specifying the location search keywords in the HTTP header "x-location-search" which is the default header that our plugin will look at. (We've not specified any plugin configuration in our declarative configuration.)
As you can see, our custom plugin has looked up the location address, latitude, and longitude and added them as upstream headers which are reflected by the httpbin response.
Specifying plugin configuration
Suppose, we want the plugin to lookup the search keywords in the header "x-search" — and we want the location address to be added to a header "x-location-addr". We can set these in the plugin configuration, because remember: we wrote our plugin to be intelligent. Head over to the declarative configuration in the dbless_config directory and edit kong.yaml to specify these in the plugin configuration:
If we restart our API gateway by pressing Ctrl+C then running docker-compose up again, we should now be able to make a request to localhost:8000, specifying the search location in the ‘x-search' header:
In a few lines of Python code, we have a working Kong Gateway plugin, complete with configuration options and exception handling!
What we've built together is a trivial plugin, but using the environment provided and what you've learned about Kong's configuration, you can go ahead and build plugins to your heart's content making full use of the wide range of libraries provided by the Python ecosystem.
If you're looking for more plugin examples, take a look at the examples directory in the kong-python-pdk.