Image by Prawny on Pixabay

Google Cloud functions with OpenCage Geocoder API

First of all, why a function as a service to wrap a REST API ? 
You, may, don’t want to expose your own OpenCage API Key to your client end users; you may want to analyse requests sent to OpenCage, etc. 
So here is a solution to proxy the requests on your own Google Cloud infrastructure.

Tutorial

We will first need a Google account to activate the Google Cloud Platform. Then, we will be using the Serverless Framework from serverless.com.

Prerequisites

  • node with npm or yarn, or a js package manager or your choice
  • serverless: for convenience you can install it globally
    $ npm i -g serverless

Assuming serverless has been setup globally, the sls and serverless commands are both available from your command line.

Google — Credentials

The Serverless Framework needs access to account credentials for your Google Cloud account so that it can create and manage resources on your behalf.

Create a Google Cloud Account

We need a Billing Account with a credit card attached to use Google Cloud Functions. Here’s how to create one:

  • Create the account.
  • Enter the name of the Billing Account and enter your billing information. And click Submit to enable billing.
  • A new Billing Account will offer you a free trial.

If necessary, a more detailed guide on creating a Billing Account can be found here.

Create a new Google Cloud Project

A Google Cloud Project is required to use Google Cloud Functions. Here’s how to create one:

  • Go to the Google Cloud Console.
  • There is a dropdown near the top left of the screen (near the search bar that lists your projects). Click it and select Create Project.
  • Enter a Project name and select the Billing Account you created in the steps above.
  • Click on Create to start the creation process.
  • Wait until the Project was successfully created and Google will redirect you to your new Project.
  • Verify your currently within your new Project by looking at the dropdown next to the search bar. This should mark your new Project as selected.

Enable the necessary APIs

You need to enable the following APIs so that Serverless can create the corresponding resources.

Go to the API dashboard, select your project and enable the following APIs:

  • Google Cloud Functions
  • Google Cloud Deployment Manager
  • Google Cloud Storage
  • Stackdriver Logging

Get credentials

You need to create credentials Serverless can use to create resources in your Project.

  • Go to the Google Cloud API Manager and select Credentials on the left.
  • Click on Create credentials and select Service account key.
  • Select New service account in the Service account dropdown.
  • Enter a name for your Service account name (e.g. "opencage-function").
  • Select Project --> Owner as the Role.
  • Key type is JSON.
  • Click on Create to create your private key.
  • The so called keyfile will be downloaded on your machine.
  • Save the keyfile somewhere secure. I recommend making a folder in your home folder and putting it there; like this, ~/.gcloud/keyfile.json. You can change the file name from keyfile to anything according to the goolge project name. Just try to remember the path you saved it to!

Create a new project from a serverless boilerplate

Serverless will help up us to bootstrap the project with the following command:

$ serverless create --template google-nodejs --path gcloud-functions-opencage-geocoder

and it will output:

Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "[...]/gcloud-functions-opencage-geocoder"
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v1.32.0
-------'
  Serverless: Successfully generated boilerplate for template: "google-nodejs"

now let’s get into the folder to have a look at what’s been created:

$ cd gcloud-functions-opencage-geocoder/
$ ls -althat will output this directory structure:
.
├── .gitignore
├── index.js
├── package.json
└── serverless.yml

Lock down the framework version

By adding serverless as a development dependency, we will be ensure to get a compatible version, without using the global version (useful for your CI):

$ npm i -D serverless

Update the service name in serverless.yml

Open up your serverless.yml file and update the service name to opencage-geocoder

service: opencage-geocoder

Update the provider config in serverless.yml

Open up your serverless.yml file and update the provider section with your Google Cloud Project id and the path to your keyfile file. It should look something like this:

Like in the example above, feel free to use node platform version 8 instead of 6. Indeed, node 6 is getting old and big fans of async/await will be pleased with the version 8.

Google Cloud function are not generally available, here the region parameter is set to us-central1.

Install dependencies

We will hide OpenCage Geocoder API Key and we will set it in an environment file, so we will use the dotenv library, then the OpenCage API request will be held by the opencage-api-client library

$ npm i -S dotenv opencage-api-client

Serverless helpers

Serverless comes will some useful plugins, We will use the environment generator file plugin. Then we can, with this plugin, generate an env file containing the OpenCage API Key. If you have different API Key for your different environments (development, staging, production), the plugin still help storing all the different value regarding the stage and deploying only the targeted stage value

$ npm i -D serverless-env-generator

Edit serverless.yml file, adding this after provider: section

Environment variable

Use the serverless env generator to create aenvironment.yml file. This file will hold the OpenCage Geocoder API Key.

$ serverless env --attribute OCD_API_KEY --value <YOUR-OPEN-CAGE-API-KEY> --stage dev
Serverless: Successfuly set OCD_API_KEY 🎉

Now generate a .env file manually.

$ serverless env generate
Serverless: Creating .env file...

This operation is not needed when deploying, it will be done automatically by the serverless deployment process. For security reasons, the .env file is removed after the deployment. I recommend, for the same reasons, to avoid to store these files into the repository.

Let’s code it

Open the index.js file and remove its content. Replace it by :

We start using dotenv to import the API Key from the .env file.

GCloud functions for NodeJS 8 uses the same interface as ExpressJS. Actually the runtime uses ExpressJS to handle the incoming requests.

The Input Query Parameters can be found in therequest.query object.

To proxy the API Key, we first check if a key is part of the input query string. If not we add the one from the environment variable. And in case nothing was in input query parameters and the env var was not set, we return directly an 403 error.

We do not override the API Key, if one was in as an input parameter.

Then the query is sent to the OpenCage Geocoder API endpoint via the client library. The response is sent back as is.

Edit serverless handler

As we exported opencage from our module, we have to specify it in the serverless yml file. opencage will be the function name, geocode is the handler and we will use geocode for the path

Deploy

$ serverless deploy
Serverless: Creating .env file...
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Removed .env file
Serverless: Compiling function "opencage"...
Serverless: Creating deployment...
Serverless: Checking deployment create progress...
..
Serverless: Done...
Serverless: Uploading artifacts...
Serverless: Artifacts successfully uploaded...
Serverless: Updating deployment...
Serverless: Checking deployment update progress...
..........
Serverless: Done...
Service Information
service: opencage-geocoder
project: opencage-123456
stage: dev
region: us-central1
Deployed functions
opencage
https://us-central1-opencage-123456.cloudfunctions.net/geocode

The last line of the deployments gives us the HTTP endpoint.

Test

Now we can test our new endpoint with geocoding and reverse geocoding operations:

  • Geocode Piccadilly Circus London
$ curl --request GET \
--url "https://us-central1-opencage-123456.cloudfunctions.net/geocode?q=Piccadilly%20Circus%20London"
  • Reverse Geocode -22.6792, 14.5272
$ curl --request GET \
--url "https://us-central1-opencage-123456.cloudfunctions.net/geocode?q=-22.6792%2C+14.527"

To go further

If you look at the associated repository on Github, you will find some unit tests.

Cloud Functions are at the General Availability release level since the 24th of July 2018. But Node.js 8 is still in beta. And Cloud Functions are available only in the following regions for now:

  • us-central1 (Iowa)
  • us-east1 (South Carolina)
  • europe-west1 (Belgium)
  • asia-northeast1 (Tokyo)

The end

I hope you enjoyed this tutorial. Feel free to reach me with whatever channel suits you for comments, issue, or coffee!

Links