Following my latest post about a Go package to validate UK bank account numbers, I wanted to offer a public API to let people check if a UK bank account number is valid or not. I know that offering a Go package is not ideal for everyone because for the moment Go is not everywhere in the tech ecosystem, and it’s always convenient to have an API you can send requests to, especially in a frontend context. My goal was to offer a JSON API, supporting authentication thanks to a HTTP header and with rate limits. With this, in the future you could adapt rate limits to some API keys, if you want to allow a larger amount of requests for some clients.
Packages I used
I wanted to give cloudflare/service a go because it lets you build quickly JSON APIs with some default endpoints for heartbeat, version information, statistics and monitoring. I used etcinit/speedbump to offer the rate limiting functionality and it was very easy to use. Note that the rate limiting functionality requires a Redis server to store request counts. Finally, I used the famous codegangsta/negroni to create middlewares to handle API authentication and rate limits and keeping my only controller relatively clean.
Deploying behind Nginx
My constraints were the following:
- The API should only be accessible via HTTPS and HTTP should redirect to HTTPS.
- The Golang server should run on a port > 1024 and the firewall will block access to everything but ports 22, 80 and 443
- The only endpoints that should be exposed to the public are
/verify
,/version
and/heartbeat
. Statistics and monitoring should be accessible by administrators on localhost through HTTP
I ended up with this Nginx virtual host to suit my needs, I’m not sure if it can be simpler:
geo $is_localhost { default 0; 127.0.0.1/32 1; } server { listen 80; listen 443 ssl; server_name modulus.antoine-augusti.fr localhost.antoine-augusti.fr; ssl_certificate /etc/nginx/ssl/modulus.antoine-augusti.fr.crt; ssl_certificate_key /etc/nginx/ssl/modulus.antoine-augusti.fr.key; if ($is_localhost) { set $test A; } if ($scheme = http) { set $test "${test}B"; } # Redirect to HTTPS if not connecting from localhost if ($test = B) { return 301 https://$server_name$request_uri; } # Only the following endpoints are accessible to people not on localhost location ~ ^/(verify|heartbeat|version) { include sites-available/includes/dispatch-golang-server; } # Default case location / { # Not on localhost? End of game if ($is_localhost = 0) { return 403; } # Forward request for people on localhost include sites-available/includes/dispatch-golang-server; } }
And for sites-available/includes/dispatch-golang-server
:
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $host; proxy_pass http://127.0.0.1:8080;
With this, I can still access the reserved endpoints by opening a SSH tunnel first with ssh -L4242:127.0.0.1:80 [email protected]
and going to http://localhost.antoine-augusti.fr:4242/stats
after.
Note that the Golang server is running on port 8080 and it should be monitored by Supervisor or whatever you want to use.
Grabbing the code and a working example
First of all, the API is available on GitHub under the MIT license so that you can deploy and adapt it yourself. If you want to test it first, you can use the API key foo
against the base domain https://modulus.antoine-augusti.fr
. Here is a cURL call for the sake of the example:
curl -H "Content-Type: application/json" -H "Api-Key: foo" -X POST -d '{"sort_code": "308037", "account_number": "12345678"}' https://modulus.antoine-augusti.fr/verify
Note that this API key is limited to 5 requests per minute. You’ve been warned 🙂 Looking for more requests per month or SLA, drop me a line.