How it works...

There's quite a bit going on in this recipe, so we'll go through it section by section. Firstly, the core NGINX configuration has two main sections. The first is the proxy location block directive, which is what we use to make external requests. This is because within route.lua, the location.capture function only works for internal requests. While, in this recipe, we've only made an internal request, having this proxy_pass directive allows us to easily incorporate external calls. It simply takes the remote request as part of the URI to pass through. We've also locked it down (using allow / deny) to the localhost to prevent any external misuse.

Next, we define our API location block directive. To keep the code within the main configuration file neat and precise, we store the configuration in an external file so that the code management is easier.

Our auth.lua file contains our authentication code. For the sake of keeping this recipe simple to follow, I've created a basic table type (which is an associative array) to store a few test API keys in; for a production system, these would be pulled from an external data source.

We then define a badAuth function, which gives us an easy way to return an HTTP 401 error to let the client connection know the connection wasn't authorized.

The next function we've defined is isAuthorised. This simply iterates through our table of allowed API keys to determine whether there is a match or not.

Lastly, we extract the X-API-KEY header to interrogate the value. If it's nil, that is, the header hasn't been set, we use badAuth to return 401. If it's not nil, we use the isAuthorised function to determine whether there is a match or not. If there's a match, there's simply nothing further for our code to do and OpenResty will start processing the content components.

This brings us to our routing code contained within the route.lua file. Like our previous recipe, we make a connection to our Redis server. This is used to provide dynamic routing. This means, to change our endpoints or even to provide new API functions, there's no requirement to restart our API gateway to detect these changes.

To get the endpoint URI to call, we use ngx.var.uri as the key. In our example, this has been configured as /api/v1/test. If this exists, we use ngx.location.capture to proxy this through and retrieve the data. A successful return of data is then sent back to the client, parsed as JSON using the CJSON module. In the case of an error, we simply return an error message and set the HTTP status to 500.