Hi. This is a showcase of a recent project I built which is a load balanced API gateway in GCP. In this article I'll walk you through the entire architecture so you can understand how each part works. My reason for building this project was to get more hands-on experience with varied GCP tools.
Link to code:
https://github.com/eduzgun/api-gateway-footy
Overview
Code
The web app is a simple Premier League football application that shows the current and upcoming fixtures of the ongoing gameweek. It also displays the Premier League table standings. This data is fetched from an API provider called RapidAPI. Users can also simulate booking tickets for the games.
The technology used to built this stack is called the GOTTH stack.
GO - The backend is written in Go using the Go Gin web framework library. https://pkg.go.dev/github.com/gin-gonic/gin
T - TailwindCSS is a utility-first CSS framework allowing web applications to be styled directly in HTML. https://tailwindcss.com/
T - Templ is a Go-based templating engine that offers a type-safe way to generate HTML. https://templ.guide/
H - HTMX leverages HTML to handle most frontend rendering logic, swapping out chunks of HTML dynamically in response to server requests. No Javascript needs to be written! https://htmx.org/
This stack makes development super easy. Go is already efficient as it is, and when combined with these other tools, it allows us to build minimal yet high-performing apps. Additionally, I am using Postgres as the database, Makefile for run commands, and Air for live reloading in Go.
I have also used Terraform to construct all the cloud infrastructure for the project. Resources were deployed systematically this way.
VPC, Subnets and Firewalls
I have a custom VPC (Virtual Private Cloud) which covers the scope of the whole project. The VPC provides network connectivity between my cloud resources such as conencting VM instances to the load balancer. This VPC enhances security by isolating resources within a private network, allowing only authorized access to applications.
Within my Virtual Private Cloud (VPC), I've established two essential subnets: "eu-subnet" and "us-subnet." These house my Europe and United States virtual machines and are needed for load balancing globally.
The VPC network is composed of 4 firewall rules as shown above. They provide access to only necessary ports such as 8080 for the application and 5432 for the Postgres instance. This selective control prevents unauthorized access, enhancing network security.
VM instances
My EU and US VMs run both the Dockerized Golang application and the Postgres database. The VMs must be configured correctly by authenticating to Google Artifact Registry, where my Docker image is stored. The application runs on port 8080, and the database runs on port 5432. These VMs can only be accessed through the HTTP protocol.
Also, each VM belongs to its respective unmanaged instance group.
Load balancer
The load balancer for this project is a global external load balancer. It distributes requests to either the EU or US virtual machines based on proximity or availability. In simpler terms, it ensures users are connected to the closest or most accessible server.
This load balancer can be accessed via HTTP or HTTPS on the frontend. On the backend, it connects to a single backend service called "entire-app," which includes the EU and US instance groups.
The load balancer also caches static content using Cloud CDN, which reduces latency for end-users.
Cloud Function
In this project, I have a Cloud Function that serves as the interface to RapidAPI. It logs in as the admin user to my backend. I have written the code so that only the admin user can send requests to RapidAPI.
For example, when triggered with a GET request to "/api/fixtures/next," the function authenticates as an admin to my application running in both the EU and US regions. It then sends a request to the API controller, which retrieves the latest Premier League fixtures from RapidAPI. Finally, these fixtures are updated in the database.
It also handles requests for "/api/fixtures/current" and "/api/standings/{leagueID}."
Instead of putting all the database update tasks directly into the cloud function, I'm using it more like a bridge. It acts as an administrative user within my application, allowing the existing app logic to handle the actual database updates.
API gateway
The API gateway is the highlight of this project. It offers features such as authentication, SSL termination, rate limiting, and logging. Here’s how it works in my project: when you access the API gateway without extra path parameters, you are directed to the football app. When you include the "/api" URL path, you are directed to the cloud function. The API gateway provides a single entry point for the entire project.
The API gateway is built using a configuration YAML file that follows the OpenAPI 2.0 format. This file defines which paths go to which backend.
Here, you can compare the DELETE "/tickets/{gameID}" path and the GET "/api/standings/{leagueID}" path. The ticket functionality belongs to the app, so its "x-google-backend" is the IP address of the load balancer. Anything with "/api" belongs to the cloud function, so its backend is the cloud function URL. Also, notice that GET "/api/standings/{leagueID}" has a field called security, which requires an API key, but DELETE "/tickets/{gameID}" does not. This is because any user can access the football application through the API gateway, but only authenticated users (me) can access the Cloud Function if they provide a valid key. It looks like this:
When we want to access the web app, the API gateway points us to the web app. When we want to access the API, the API gateway points us to the API.
Final product
Login page
Home page
Booking page
That's all! I hope you enjoyed this showcase of my API gateway project in GCP. It's been fun to work with these technologies and I encourage you to also build a similar project. Feel free to ask me any questions about this project, or talk about anything in general.
And be sure to look at my other blogs on terminal3
Message me on LinkedIn: My LinkedIn