API easter eggs
Tags: showdev, api, webdev, kong
Published: 2020-06-02 10:31:18

## Intro

I am __27 y.o.__ and I rarely play video games now. Believe me, I am such a boring 💤 guy, that in 90% of cases I would choose to open my favorite code editor instead of Steam, which is covered in dust already. And definetly I do not miss the times when I could be lost for weeks in The Elder Scrolls or Grand Theft Auto. But each time something reminds me of a good old gaming title, a wide-wide smile spreads across my face. Even now, when I write this lines, I can't stop myself from smiling :)
 
Games 🎮 always had something in them: you felt the real __spirit of exploration__, you rushed with joy to your friends just to seat nearby and watch them play! And I bet that all of us had a __brilliant__ idea of a game that everyone would love (*if we had a chance to implement it*). Becoming an astronaut was never an option, as there was always a choice to become a Game Developer.

You can prove me wrong in this [poll](https://www.pollsify.com/c/e51yA5) 🗳️!

Now, 15 years later, I am actually happy that my dreams have only partially come true. I am a developer, but with less "[blood, sweat and pixels](https://www.amazon.com/Blood-Sweat-Pixels-Triumphant-Turbulent/dp/0062651234)". Instead of creating fancy drawn worlds, I have been creating new web ecosystems, platforms and APIs. It definetly sounds like a more boring job, but games have taught me that there is always field for fantasy and exploration. *Especially, where it's least expected.*

__So what if I told you, that APIs could also have some [Easter Eggs](https://en.wikipedia.org/wiki/Easter_egg_(media))?__ 🐣

Of course I am not talking about unplanned security holes, 0-day vulnerabilities or public APIs, where the main easter egg is the outdated documentation. Technically they count, but I would suggest a more positive approach. Let's include some fun in our API responses!

In order to do that, we will write our own [Lua](https://www.lua.org/about.html) plugin for [Kong API Gateway](https://konghq.com/kong/). Don't worry if you are not familiar with Lua or Kong. It took me just one working day to write a much more complex working plugin without any previous knowledge. Plus I'll give a brief explanation and share some useful links to make your life easier. In any case, these tools may be a great addition to your technical arsenal, so I recommend you to spend some time afterwards to get to know them closer. If you are familiar with Kong and Lua, you may skip the next abstract and jump straight to [Preparing to cook](#preparing-to%C2%A0cook).

## Kong and Lua


![](https://cdn-images-1.medium.com/max/1600/0*Gkc25sYufZqOCudL.png)

Kong Gateway 🦍 is the the world's most popular open source [API gateway](https://microservices.io/patterns/apigateway.html). It is cloud-native, super fast, scalable and extendable. Kong itself extends underlying [OpenResty](https://openresty.org/en/) web platform (based on LuaJIT), which runs on the ultra-performant [Nginx](https://nginx.org/en/) core (written in C). Kong can run on any platform and in any environment: for example, it's widely used as an [Ingress controller](https://konghq.com/solutions/kubernetes-ingress/) for Kubernetes cluster. But the true power of Kong Gateway is the rich [ecosystem of ready-to-use plugins](https://docs.konghq.com/hub/) which cover all the important cases: authentication, bot detection, proxy caching, rate limiting, request termination and so on. There are plenty of various useful examples on [Kong Github](https://github.com/Kong/kong/tree/master/kong/plugins). Though, before diving into source code I would strongly recommend to read [official plugin development guide](https://docs.konghq.com/2.0.x/plugin-development/) together with the article on [declarative configuration and dbless mode](https://docs.konghq.com/2.0.x/db-less-and-declarative-config/).

> It’s very important to understand, that [Kong](https://konghq.com/) is much more than a perfect API Gateway. Kong is a full-fledged [open-core](https://en.wikipedia.org/wiki/Open-core_model) API platform for powering of all your APIs and microservices. Kong is a prefect example of an innovative company which moves forward together with its open source community.

![](https://miro.medium.com/max/512/0*t7ScoKsfK50u0tCN.gif)

Lua 🌝 is a powerful, efficient, lightweight, embeddable scripting language. It is widely used in web (OpenResty, Redis, etc.) and is loved in [gaming industry](https://en.wikipedia.org/wiki/Category:Lua-scripted_video_games) (WoW, Dark Souls, Star Wars series, etc.). As for me, that sounds like a perfect combination!

Here is a simple Lua [tutorial](https://www.tutorialspoint.com/lua/index.htm) to get started, but for more detailed information you can use [official reference manual](https://www.lua.org/manual/5.3/). It’s not required to read any of the above as our plugin code will be deadly simple. And last but not least, we will use [LuaRocks](https://luarocks.org/) package manager for plugin integration into Kong.

## Preparing to cook

Cooking API easter eggs doesn't require much knowledge or skill. Here is what we need to get started:
* one pot with water;
* two easter eggs: "#1 Disco" (3 min) and "#2 Jackpot" (6 min);
* one `docker-compose` installed;
* cloned [recipe](https://github.com/maxipavlovic/kong-easter-eggs) from GitHub.

__\#1 Disco__ 💃🏿🕺: Any good APIs must know how to make you dance (and I am not talking about dancing around with their documentation). When anyone will add “__Make-Me: dance__” header to *GET* or *HEAD* API request, we will return them a link with an incendiary dance.

__\#2 Jackpot__ 💰: We will count all the incoming requests and every N responses will contain a special header “__You-Have-Won: a *prize*__”. A prize must be randomly chosen and must be something trendy that anyone would like to have.

## Water is boiling

Initially our plugin has the following structure:

```
src/
    access.lua
    handler.lua
    header_filter.lua
    schema.lua

docker-compose.yml
kong-easter-eggs-1.0.0-3.rockspec
kong.tpl.yml
run.sh
```

Plugin sources live in `src` folder only. `.rockspec` file is a specification for *luarocks* package manager. `docker-compose.yml` and `run.sh` are used in plugin development and testing. `kong.tpl.yml` is a template for our declarative configuration.

> Kong plugin file structure has some [strict rules](https://docs.konghq.com/2.0.x/plugin-development/file-structure/). There must always `handler.lua` entry point file and `schema.lua`, where plugin configuration is described. `access.lua` and `header_filter.lua` are our custom plugin files. They could have any name, but it’s a good practice to name them like that to reflect the [lifecycle of requests](https://docs.konghq.com/2.0.x/plugin-development/custom-logic/#available-contexts) in Kong.

After setting of project layout we need to create declarative configuration for Kong to start with:

```
cp kong.tpl.yml kong.yml 
```
We will now modify `kong.yml` to have the following configuration:

{% gist https://gist.github.com/maxipavlovic/95aca96be39e29160ae363a67543f131 %}

We have just added Nanoservice with named `/` root to our Kong API Configuration. We have also set some parameters like `dance_link` to our `kong-easter-eggs` plugin.

> Note: Microservices are a doubtful trend now, so we need a fresh cool title. Nanoservice title has nothing to do with the size of a service, it’s just a great API service created in the Nano text editor.

All of kong plugin parameters must be described in `schema.lua` file. Parameters may have different types (string, number, array, etc.), they may be *required* or have *default* values.

{% gist https://gist.github.com/maxipavlovic/c405dd6dde8d95b32370ca80653f9200 %}

Let’s now look at our main entry point `handler.lua`:

{% gist https://gist.github.com/maxipavlovic/4ff8c06877f8a31c0fe3f82b45362f52 %}

*Handler* contains imports from other files. We also set plugin priority and initialize plugin instance. Our plugin doesn’t depend on any other plugin, so priority value doesn’t matter much. But if we would want to include easter eggs only after authentication, then we would need to choose priority wisely, according to this [priority execution list](https://docs.konghq.com/2.0.x/plugin-development/custom-logic/#plugins-execution-order). Within plugin scope execution sequence will be the following: `new` —> `access` —> `header_filter`.

We are totally ready to cook, so let’s start Kong Gateway immediately.

```
docker-compose up
```

If everything is fine, we will see something like this in console:

```
kong_1  | 
kong_1  | No existing manifest. Attempting to rebuild...
kong_1  | kong-easter-eggs 1.0.0-1 is now installed in /home/kong/.luarocks (license: MIT)
kong_1  | 
kong_1  | 2020/05/31 16:49:36 [notice] 67#0: using the "epoll" event method
kong_1  | 2020/05/31 16:49:36 [notice] 67#0: openresty/1.15.8.3
kong_1  | 2020/05/31 16:49:36 [notice] 67#0: built by gcc 9.2.0 (Alpine 9.2.0) 
kong_1  | 2020/05/31 16:49:36 [notice] 67#0: OS: Linux 4.9.184-linuxkit
kong_1  | 2020/05/31 16:49:36 [notice] 67#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
kong_1  | 2020/05/31 16:49:36 [notice] 67#0: start worker processes
kong_1  | 2020/05/31 16:49:36 [notice] 67#0: start worker process 71
...
```

To be totally sure, we can already make a request to Kong, which runs on port 8000 as described in our `docker-compose.yml`:

```
curl localhost:8000 -I

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 648
Connection: keep-alive
Content-Encoding: gzip
Accept-Ranges: bytes
...
```

## Cooking Disco (3 min)

When we get “__Make-Me__” header, we don’t even want to make a call to the upstream service. We need to interfere into requests lifecycle as soon as possible and `access` phase is one of the options to do that. Let’s look at our sources:

{% gist https://gist.github.com/maxipavlovic/dbda81fc847a4cc2db854a9bd4b648bc %}

To understand code fully, you need to know that Kong is shipped with a set of helpful tools called PDK — [Plugin Development Kit](https://docs.konghq.com/2.0.x/pdk/). `kong.request` and `kong.response` are examples of modules, provided by Kong to access request information and process responses. Let’s check if our egg is ready:

```
curl localhost:8000 -H "Make-Me: rich"

I'm a teapot. I can't make you rich!
```

Well… Could be better, but this was expected, as we only taught our APIs dancing techniques. Let’s try again:

```
curl localhost:8000 -H "Make-Me: dance" -I

HTTP/1.1 302 Moved Temporarily
Date: Sun, 31 May 2020 22:19:24 GMT
Content-Type: text/html
Content-Length: 110
Connection: keep-alive
Location: https://youtu.be/dQw4w9WgXcQ
X-Kong-Response-Latency: 0
Server: kong/2.0.3
```

Awesome! We are redirected to our [dancing video](https://youtu.be/dQw4w9WgXcQ) in `Location` header. Disco is ready and now we can check Jackpot.

## Hitting Jackpot (6 mins)

Let’s dive into code straight away:

{% gist https://gist.github.com/maxipavlovic/9130abf71d4ebef4fe57fea0558fcda5 %}

You may have spotted, that we use some dirty magic in this line `ngx.shared.kong:incr(“requests_count”, 1, 0)`. Kong runs several worker processes, which run simultaneously. To count incoming requests we need to use the [shared memory](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#ngxshareddict) provided by OpenResty. Kong provides a [caching abstraction](https://docs.konghq.com/2.0.x/plugin-development/entities-cache/#caching-custom-entities) via `kong.cache`, but I have chosen to demonstrate another approach. From code we can see that we give out the prize only in case of successful response (*≤399*) and if there are any prizes. Let’s go and hit our well-deserved jackpot!

```
curl localhost:8000 -I

...
X-Cache: HIT
You-Have-Won: Ticket to Crew Dragon
X-Kong-Upstream-Latency: 557
...
```

In every third response you will see “__You-Have-Won__” header. My jackpot is the *Ticket to Crew Dragon*. Nice one, but I am sure you can do better. Try yourself to see what you will win! APIs must always be rewarding as they really love to be used.

## Setting the table

Now, when our easter eggs are ready, we need to color and pack them, so we could share them with the world. *Luarocks* helps us with that and you can already use them in your Kong API Gateways:

```
luarocks install kong-easter-eggs
```

> There are [several options](https://docs.konghq.com/2.0.x/plugin-development/distribution/#installing-the-plugin) on how plugins can be installed into Kong. As plugins need to be applied before the start of Kong, you can always create a custom Dockerfile with plugin instructions, based on the official Kong docker images.

Anyway, my fantasy is limited and you may want to add more cool things to your APIs. Here is the GitHub repository for you:

{% github https://github.com/maxipavlovic/kong-easter-eggs %}

## Outro

I hope you liked the article as much as I loved writing it. Kong API Gateway is an awesome open source project, which is definetly worth reading and writing about. I hope that creation of even funny plugins, like the one from the article, helps them expand their ecosystem.
Anyway, thanks for your time. Stay inspired and share your cool API easter egg ideas in comments, GitHub or [LinkedIn](https://www.linkedin.com/in/mxms/).