Quick tip: Review the prerequisites before you run the lab
Use an Incognito or private browser window to run this lab. This prevents any conflicts between your personal account and the student account, which may cause extra charges incurred to your personal account.
Test and share your knowledge with our community!
done
Get access to over 700 hands-on labs, skill badges, and courses
Get access to over 700 hands-on labs, skill badges, and courses
GSP761
Overview
For the labs in the Serverless Cloud Run Development course, you will read through a fictitious business scenario and assist the characters with their serverless migration plan.
Twelve years ago, Lily started the Pet Theory chain of veterinary clinics. As the chain of clinics has grown, Lily spends more time on the phone with insurance companies than treating pets. If only the insurance companies could see the totals of the treatments online!
In previous labs in this series, Ruby, the computer consultant, and Patrick, the DevOps Engineer, moved Pet Theory's customer database to a serverless Firestore database in the cloud, and then opened up access so customers can make appointments online. Since Pet Theory's Ops team is a single person, they need a serverless solution that doesn't require a lot of ongoing maintenance.
In this lab, you'll help Ruby and Patrick to give insurance companies access to customer data without exposing Personal Identifiable Information (PII). You will build a secure Representational State Transfer (REST) API gateway using Cloud Run, which is serverless. This will let the insurance companies see the total cost of treatments without seeing customers' PII.
Objectives
In this lab, you will:
Develop a REST API with Go
Import test customer data into Firestore
Connect the REST API to the Firestore database
Deploy the REST API to Cloud Run
Prerequisites
This is a intermediate level lab. This assumes familiarity with the Cloud Console and Cloud Shell environments. This lab is part of a series. Taking the previous labs could be helpful, but is not necessary:
Importing Data to a Serverless Database
Build a Serverless Web App with Firebase and Firestore
Build a Serverless App that Creates PDF Files
Setup and requirements
Before you click the Start Lab button
Read these instructions. Labs are timed and you cannot pause them. The timer, which starts when you click Start Lab, shows how long Google Cloud resources are made available to you.
This hands-on lab lets you do the lab activities in a real cloud environment, not in a simulation or demo environment. It does so by giving you new, temporary credentials you use to sign in and access Google Cloud for the duration of the lab.
To complete this lab, you need:
Access to a standard internet browser (Chrome browser recommended).
Note: Use an Incognito (recommended) or private browser window to run this lab. This prevents conflicts between your personal account and the student account, which may cause extra charges incurred to your personal account.
Time to complete the lab—remember, once you start, you cannot pause a lab.
Note: Use only the student account for this lab. If you use a different Google Cloud account, you may incur charges to that account.
How to start your lab and sign in to the Google Cloud console
Click the Start Lab button. If you need to pay for the lab, a dialog opens for you to select your payment method.
On the left is the Lab Details pane with the following:
The Open Google Cloud console button
Time remaining
The temporary credentials that you must use for this lab
Other information, if needed, to step through this lab
Click Open Google Cloud console (or right-click and select Open Link in Incognito Window if you are running the Chrome browser).
The lab spins up resources, and then opens another tab that shows the Sign in page.
Tip: Arrange the tabs in separate windows, side-by-side.
Note: If you see the Choose an account dialog, click Use Another Account.
If necessary, copy the Username below and paste it into the Sign in dialog.
{{{user_0.username | "Username"}}}
You can also find the Username in the Lab Details pane.
Click Next.
Copy the Password below and paste it into the Welcome dialog.
{{{user_0.password | "Password"}}}
You can also find the Password in the Lab Details pane.
Click Next.
Important: You must use the credentials the lab provides you. Do not use your Google Cloud account credentials.
Note: Using your own Google Cloud account for this lab may incur extra charges.
Click through the subsequent pages:
Accept the terms and conditions.
Do not add recovery options or two-factor authentication (because this is a temporary account).
Do not sign up for free trials.
After a few moments, the Google Cloud console opens in this tab.
Note: To access Google Cloud products and services, click the Navigation menu or type the service or product name in the Search field.
Activate Cloud Shell
Cloud Shell is a virtual machine that is loaded with development tools. It offers a persistent 5GB home directory and runs on the Google Cloud. Cloud Shell provides command-line access to your Google Cloud resources.
Click Activate Cloud Shell at the top of the Google Cloud console.
Click through the following windows:
Continue through the Cloud Shell information window.
Authorize Cloud Shell to use your credentials to make Google Cloud API calls.
When you are connected, you are already authenticated, and the project is set to your Project_ID, . The output contains a line that declares the Project_ID for this session:
Your Cloud Platform project in this session is set to {{{project_0.project_id | "PROJECT_ID"}}}
gcloud is the command-line tool for Google Cloud. It comes pre-installed on Cloud Shell and supports tab-completion.
(Optional) You can list the active account name with this command:
gcloud auth list
Click Authorize.
Output:
ACTIVE: *
ACCOUNT: {{{user_0.username | "ACCOUNT"}}}
To set the active account, run:
$ gcloud config set account `ACCOUNT`
(Optional) You can list the project ID with this command:
gcloud config list project
Output:
[core]
project = {{{project_0.project_id | "PROJECT_ID"}}}
Note: For full documentation of gcloud, in Google Cloud, refer to the gcloud CLI overview guide.
Lily, Founder of Pet Theory
Hi Ruby,
Remember our conversation last week when I expressed how swamped I am with paperwork and phone calls from the insurance company? If only there was a way to allow the representatives to access customer records in an efficient, secure way.
This current level of workload isn't sustainable. Can you help?
Lily
Ruby, Software Consultant
Hi Lily,
Yesterday I had lunch with Patrick and we drew up a plan to make it easier for authorized 3rd parties to securely access Pet Theory's digital records.
We will build this in four steps:
Build a simple REST API.
Import customer test data.
Connect the REST API to the customer database.
Add authentication to the REST API.
Patrick and I already have the skill set for steps 1 + 2, so we are off to a good start. We plan to have a working prototype by the end of the week.
Ruby
Help Ruby manage the activities necessary to build the REST API for Pet Theory.
Task 1. Enable Google APIs
For this lab, 2 APIs have been enabled for you:
Name
API
Cloud Build
cloudbuild.googleapis.com
Cloud Run Admin
run.googleapis.com
Task 2. Developing the REST API
Activate your project:
gcloud config set project $(gcloud projects list --format='value(PROJECT_ID)' --filter='qwiklabs-gcp')
Clone the pet-theory repository and access the source code:
git clone https://github.com/rosera/pet-theory.git && cd pet-theory/lab08
Use your favorite text editor, or use the Code Editor button in the Cloud Shell ribbon, to view the go.mod and go.sum files.
Create the file main.go and add the below contents to the file:
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/v1/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "{status: 'running'}")
})
log.Println("Pets REST API listening on port", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatalf("Error launching Pets REST API server: %v", err)
}
}
Note:
In the above code, you create an endpoint to test that the service is up and running as expected. By appending "/v1/" to the service URL, you can verify the application is functioning as expected. Cloud Run deploys containers, so you need to provide a container definition. A file named `Dockerfile` tells Cloud Run which Go version to use, which files to include in the app, and how to start the code.
Now create a file named Dockerfile and add the following to it:
FROM gcr.io/distroless/base-debian12
WORKDIR /usr/src/app
COPY server .
CMD [ "/usr/src/app/server" ]
The file server is the execution binary built from main.go.
Run the following command to build the binary:
go build -o server
After running the build command, make sure that you have the necessary Dockerfile and server in the same directory:
ls -la
.
├── Dockerfile
├── go.mod
├── go.sum
├── main.go
└── server
For most Cloud Run Go based apps, a template Dockerfile like the one above can typically be used without modifying it.
This command builds a container with your code and puts it in the Container Registry of your project. You can see the container if you click: Navigation menu (), click View All Products > CI/CD > Artifact Registry and click gcr.io repository. If you don't see rest-api, click Refresh.
Click Check my progress to verify that you've performed the above task.
Build an image with Cloud Build
Once the container has been built, deploy it:
gcloud run deploy rest-api \
--image gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.1 \
--platform managed \
--region {{{ project_0.default_region | "Filled in at lab startup." }}} \
--allow-unauthenticated \
--max-instances=2
When the deployment is complete, you will see a message like this:
Service [rest-api] revision [rest-api-00001] has been deployed and is serving
traffic at https://rest-api-[hash].a.run.app
Click Check my progress to verify that you've performed the above task.
REST API service deployed
Click on the Service URL at the end of that message to open it in a new browser tab. Append /v1/ to the end of the URL and then press Enter.
You should see this message:
The REST API is up and running. With the prototype service available, in the next section the API will be used to retrieve "customer" information from a Firestore database.
Task 3. Import test customer data
Ruby, Software Consultant
Hey Patrick,
Do you still have the pseudo customer data we created a while back? We will need that for testing.
Do you remember how to set up a Firestore database and import data?
Ruby
Patrick, IT Administrator
Hi Ruby,
Yes, I still have the test data. I will migrate it to Firestore today so you can use it for testing.
Patrick
Ruby and Patrick have previously created a test database of 10 customers, with some proposed treatments for one customer's cat.
Help Patrick configure the Firestore database and import the customer test data. First, enable Firestore in your project.
Return to the Cloud Console and click the Navigation menu (), click View All Products > Databases > Firestore.
Click the Create Database button.
Click the Native Mode button and click Continue.
For Location type select Region.
Select the region from the list available and click Create Database.
Wait for the database to be created before proceeding.
Click Check my progress to verify that you've performed the above task.
Firestore database created
Migrate the import files into a Cloud Storage bucket that has been created for you:
gsutil mb -c standard -l {{{ project_0.default_region | Region }}} gs://$GOOGLE_CLOUD_PROJECT-customer
gsutil cp -r gs://spls/gsp645/2019-10-06T20:10:37_43617 gs://$GOOGLE_CLOUD_PROJECT-customer
Reload the Cloud Console browser to see the Firestore results.
In Firestore, click customers under "Default". You should see the imported pet data, browse around. If you don't see any data, try refreshing the page.
Nice work, the Firestore database has been successfully created and populated with test data!
Task 4. Connect the REST API to the Firestore database
Ruby, Software Consultant
Hi Lily,
Just a quick update: Patrick and I have completed the first two tasks on the list.
Now I'm moving on to structuring the REST API so it can access the customer data in Firestore.
Ruby
Lily, Founder of Pet Theory
Hi Ruby,
Great work, Ruby! Looking forward to seeing the next stage in action.
Lily
In this section you'll help Ruby create another end-point in the REST API that will look like this:
For example, that URL should return the total amounts for all proposed, accepted, and rejected treatments for the customer with id 22530, if they exist in the Firestore database:
{
"status": "success",
"data": {
"proposed": 1602,
"approved": 585,
"rejected": 489
}
}
Note: If the customer doesn't exist in the database, status code 404 (not found) and an error message should be returned instead.
This new functionality requires a package to access the Firestore database and another one to handle cross-origin resource sharing (CORS).
Get the value of the $GOOGLE_CLOUD_PROJECT environment variable
echo $GOOGLE_CLOUD_PROJECT
Open the existing main.go file in the pet-theory/lab08 directory.
Note: Update the contents of main.go using the value shown for $GOOGLE_CLOUD_PROJECT.
Replace the content of the file with the code below, ensure the PROJECT_ID is set to :
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"cloud.google.com/go/firestore"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"google.golang.org/api/iterator"
)
var client *firestore.Client
func main() {
var err error
ctx := context.Background()
client, err = firestore.NewClient(ctx, "{{{ project_0.project_id | \"Filled in at lab startup\"}}}")
if err != nil {
log.Fatalf("Error initializing Cloud Firestore client: %v", err)
}
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
r := mux.NewRouter()
r.HandleFunc("/v1/", rootHandler)
r.HandleFunc("/v1/customer/{id}", customerHandler)
log.Println("Pets REST API listening on port", port)
cors := handlers.CORS(
handlers.AllowedHeaders([]string{"X-Requested-With", "Authorization", "Origin"}),
handlers.AllowedOrigins([]string{"https://storage.googleapis.com"}),
handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "OPTIONS", "PATCH", "CONNECT"}),
)
if err := http.ListenAndServe(":"+port, cors(r)); err != nil {
log.Fatalf("Error launching Pets REST API server: %v", err)
}
}
type Customer struct {
Email string `firestore:"email"`
ID string `firestore:"id"`
Name string `firestore:"name"`
Phone string `firestore:"phone"`
}
func getCustomer(ctx context.Context, id string) (*Customer, error) {
query := client.Collection("customers").Where("id", "==", id)
iter := query.Documents(ctx)
var c Customer
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
err = doc.DataTo(&c)
if err != nil {
return nil, err
}
}
return &c, nil
}
func getAmounts(ctx context.Context, c *Customer) (map[string]int64, error) {
if c == nil {
return map[string]int64{}, fmt.Errorf("Customer should be non-nil: %v", c)
}
result := map[string]int64{
"proposed": 0,
"approved": 0,
"rejected": 0,
}
query := client.Collection(fmt.Sprintf("customers/%s/treatments", c.Email))
if query == nil {
return map[string]int64{}, fmt.Errorf("Query is nil: %v", c)
}
iter := query.Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
treatment := doc.Data()
result[treatment["status"].(string)] += treatment["cost"].(int64)
}
return result, nil
}
Save the file.
Task 6. Pop quiz
Which function responds to URLs with the pattern `/v1/customer/`getAmountscustomerHandlerWhich statement returns success to the clientfmt.Fprintf(w, `{"status": "fail", "data": "Unable to fetch amounts: %s"}fmt.Fprintf(w, fmt.Sprintf(`{"status": "success", "data": %s}Which functions read from the Firestore databasecustomerHandler and getCustomergetCustomer and getAmounts
Click Check my progress to verify the objective.
Build image revision 0.2
Deploy the updated image:
gcloud run deploy rest-api \
--image gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.2 \
--platform managed \
--region {{{ project_0.default_region | "Filled in at lab startup." }}} \
--allow-unauthenticated \
--max-instances=2
When the deployment is complete, you will see a similar message to before. The URL for your REST API did not change when you deployed the new version:
Service [rest-api] revision [rest-api-00002] has been deployed and is serving
traffic at https://rest-api-[hash].a.run.app
Go back to the browser tab that already points to that URL (with /v1/ at the end). Refresh it and make sure you get the same message as before, that indicates that the API status is still running.
Append /customer/22530 to the application URL in your browser's address bar. You should get this JSON response, listing the sum total of the customer's proposed, approved and rejected treatments:
Here are some additional client IDs you can put in the URL instead of 22530:
34216
70156 (all amounts should be zero)
12345 (client/pet doesn't exist, should return an error e.g. Query is nil)
You have built a scalable, low-maintenance, serverless REST API that reads from a database.
Congratulations!
Congratulations! In this lab, you helped Ruby and Patrick successfully build a prototype REST API for Pet Theory. You created a REST API that connects to a Firestore database and deployed it to Cloud Run. You also tested the API to ensure it works as expected.
Google Cloud training and certification
...helps you make the most of Google Cloud technologies. Our classes include technical skills and best practices to help you get up to speed quickly and continue your learning journey. We offer fundamental to advanced level training, with on-demand, live, and virtual options to suit your busy schedule. Certifications help you validate and prove your skill and expertise in Google Cloud technologies.
Manual Last Updated January 23, 2025
Lab Last Tested January 23, 2025
Copyright 2025 Google LLC All rights reserved. Google and the Google logo are trademarks of Google LLC. All other company and product names may be trademarks of the respective companies with which they are associated.
Labs create a Google Cloud project and resources for a fixed time
Labs have a time limit and no pause feature. If you end the lab, you'll have to restart from the beginning.
On the top left of your screen, click Start lab to begin
Use private browsing
Copy the provided Username and Password for the lab
Click Open console in private mode
Sign in to the Console
Sign in using your lab credentials. Using other credentials might cause errors or incur charges.
Accept the terms, and skip the recovery resource page
Don't click End lab unless you've finished the lab or want to restart it, as it will clear your work and remove the project
This content is not currently available
We will notify you via email when it becomes available
Great!
We will contact you via email if it becomes available
One lab at a time
Confirm to end all existing labs and start this one
Use private browsing to run the lab
Use an Incognito or private browser window to run this lab. This
prevents any conflicts between your personal account and the Student
account, which may cause extra charges incurred to your personal account.
Demonstrate how to build a REST API with Go and Cloud Run