GCP API Gateway Demo with Terraform / Go / Cloud Run
Tags: go, googlecloudplatform, terraform, devops
Published: 2020-09-25 14:37:14

---
title: GCP API Gateway Demo with Terraform / Go / Cloud Run
published: true
date: 2020-09-25 14:17:41 UTC
tags: golang,googlecloudplatform,terraform,devops
canonical_url: https://medium.com/@didil/gcp-api-gateway-demo-with-terraform-go-cloud-run-f76148328e06
---

*I originally posted this article on [Medium]( https://medium.com/@didil/gcp-api-gateway-demo-with-terraform-go-cloud-run-f76148328e06)*

If you’ve been building web services on GCP Cloud Functions or Cloud Run, then you might have noticed that Google Cloud has been missing an important piece of infrastructure: an **API Gateway**.

While AWS has had its [Amazon API Gateway](https://aws.amazon.com/api-gateway/) for a few years now, GCP had no equivalent service (there was [Cloud Endpoints](https://cloud.google.com/endpoints) but I think the scope is a bit different), leaving developers to build custom solutions … But not anymore ! GCP has [recently](https://cloud.google.com/blog/products/serverless/google-cloud-api-gateway-is-now-available-in-public-beta) announced the brand new Google Cloud API Gateway, so I thought I’ll give it a try. It’s still in **beta** so do take that into consideration before using it in your production systems.

#### Why do I need an API gateway ?

Looking at RedHat’s [definition](https://www.redhat.com/en/topics/api/what-does-an-api-gateway-do):

> An API gateway is an API management tool that sits between a client and a collection of backend services.

To make this more concrete and easier to understand, let’s quickly jump to the example we’ll be building in this article.

#### A tale of 2 Microservices

For this article we’ll be building a fictional app that consists of 2 backend components:

- Users service: a simple microservice with an http API that returns a list of users in JSON format, hosted on [GCP Cloud Run](https://cloud.google.com/run).
- Locations service: a simple microservice with an http API that returns a list of locations in JSON format, also hosted on [GCP Cloud Run](https://cloud.google.com/run) (but it could also be a [GCP Cloud Function](https://cloud.google.com/functions) for example).

We’d like to provide unified access to those 2 services through an API gateway. Any frontend component can then connect to the API gateway, and the gateway will then route the requests to the proper backend service. Here is are a couple of schemas that illustrate our goal:

![](https://cdn-images-1.medium.com/max/983/1*CuywLc7oZDCU_MlN2Geqag.png)<figcaption>Users Request</figcaption>

![](https://cdn-images-1.medium.com/max/982/1*9_sVZhT7lldJ43Z551nifg.png)<figcaption>Locations Request</figcaption>

### Let’s code

You can follow along using this [github repository](https://github.com/didil/gcp-api-gateway-demo) I’ve prepared. It contains all of the code and commands needed to run the project.

I’ve wrapped most shell commands for the project in this [Makefile](https://github.com/didil/gcp-api-gateway-demo/blob/master/Makefile), you can refer to it for the details about the commands:

{% gist https://gist.github.com/didil/4b826e1f4386ab75960324ba7ac4b74b %}

#### GCP setup

I’ve detailed [here](https://github.com/didil/gcp-api-gateway-demo#setup-gcloud) the preliminary steps required to setup the GCP environment for this project. It’s mostly authenticating, creating a new GCP project, a service account with the right permissions and enabling the cloud APIs.

#### Backend services

The microservices themselves are very simple Go api servers based on [chi router](https://github.com/go-chi/chi). Here is the handler section for the users service, it has a single route and returns a static list of users as JSON:

{% gist https://gist.github.com/didil/11a95733fa4ea6d99cd187144390cb43 %}

The locations service is very similar:

{% gist https://gist.github.com/didil/e5aa54247b1d529bad332fc4d6a49d4a %}

Both services are containerized, we just build and push them to [GCP Container Registry](https://cloud.google.com/container-registry) using [GCP Cloud Build](https://cloud.google.com/cloud-build) (CI/CD) so they’re ready to deploy to GCP Cloud Run.

Build/Push users service container:

```
$ # PROJECT\_ID holds the GCP project id
$ PROJECT\_ID=$PROJECT\_ID make gcr-push-users
```

Build/Push locations service container:

```
$ PROJECT\_ID=$PROJECT\_ID make gcr-push-locations
```

#### A bit of Terraform magic

We’re deploying the Cloud Run services using [Terraform](https://www.terraform.io/). If you haven’t used it yet you should check it out as it’s a fantastic **Infrastructure as Code** tool and it can make your DevOps workflows much cleaner.

The most important part of the terraform configuration is this section where we declare the cloud run service, using the service container we pushed earlier:

{% gist https://gist.github.com/didil/386fbd2046820b0f5bd593859f341190 %}

If you’re trying to deploy the project and you’re confused about the terraform variable values that you should use in the terraform.tfvars file (not committed into the repo), here are the values from my specific setup/gcp project as an example:

{% gist https://gist.github.com/didil/6c2410d976b9eba2974199f76bad9b7b %}

To start deploying services we first create a [Terraform plan](https://www.terraform.io/docs/commands/plan.html), where Terraform calculates the changes it needs to make to the infrastructure:

```
$ make tf-plan
```

Then we [apply](https://www.terraform.io/docs/commands/apply.html) the changes:

```
$ make tf-apply
```

which will produce an output similar to:

```
Outputs:

locations-svc-url = [https://locations-svc-qqx6qmklha-ew.a.run.app](https://locations-svc-qqx6qmklha-ew.a.run.app)
users-svc-url = [https://users-svc-qqx6qmklha-ew.a.run.app](https://users-svc-qqx6qmklha-ew.a.run.app)
```

The backend services are deployed and ready to use.

#### The API Gateway

We can finally move on to the API gateway. Unfortunately at the time of writing this article, GCP API Gateway doesn’t seem to be supported yet in Terraform’s Google Cloud provider, so I couldn’t include it in the Terraform configuration. However I did find this [github issue](https://github.com/hashicorp/terraform-provider-google/issues/7212) discussing its possible implementation, so support might be added in the future. As a consequence, for this step we’re back to imperative gcloud shell commands.

The most important configuration element for our API Gateway is the OpenAPI spec:

{% gist https://gist.github.com/didil/f35072b247c3ba493bd82daf80b2fbd7 %}

It describes our API and instructs the API gateway to route specific HTTP request paths to our backend services.

We can now deploy the gateway:

```
$ # PROJECT\_ID and REGION hold the GCP project id and region
$ PROJECT\_ID=$PROJECT\_ID REGION=$REGION make deploy-api-gateway
```

then we print the gateway url:

```
$ PROJECT\_ID=$PROJECT\_ID REGION=$REGION make get-gateway-url
```

which should output something like:

```
[https://gateway-demo-4s0vqu9q.ew.gateway.dev](https://gateway-demo-4s0vqu9q.ew.gateway.dev)
```

Let’s test the API gateway /users route:

```
$ curl -s [https://gateway-demo-4s0vqu9q.ew.gateway.dev/users|jq](https://gateway-demo-4s0vqu9q.ew.gateway.dev/users%7Cjq)
[
  {
    "id": 1,
    "name": "Mike"
  },
  {
    "id": 2,
    "name": "John"
  }
]
```

And now the /locations route:

```
$ curl -s [https://gateway-demo-4s0vqu9q.ew.gateway.dev/locations|jq](https://gateway-demo-4s0vqu9q.ew.gateway.dev/users%7Cjq)
[
  {
    "id": 1,
    "name": "London",
    "country": "UK"
  },
  {
    "id": 2,
    "name": "Paris",
    "country": "FR"
  }
]
```

It works, and we are done 🎉🎊.

#### Conclusion

Obviously In this simple example we’ve only scratched the surface of the capabilities such a service can provide. For example we haven’t discussed any of the more advanced features like authentication or rate limiting. Feel free to give it a try and let me know what your first impressions are. You can check out the full docs [here](https://cloud.google.com/api-gateway/docs).