Deploy a JavaScript application to Cloud Run with AlloyDB

Deploy a JavaScript application to Cloud Run with AlloyDB

About this codelab

subjectLast updated Dec 12, 2024
account_circleWritten by Luke Schlangen & Jack Wotherspoon

1. Overview

Cloud Run is a fully managed serverless platform that enables you to run stateless containers that are invocable via HTTP requests. This Codelab will demonstrate how to connect a Node.js application on Cloud Run to AlloyDB securely with a service account using IAM Authentication.

What you will learn

In this lab, you will learn how to:

  • Create an AlloyDB instance (configured to use Private Service Connect)
  • Deploy an application to Cloud Run that connects to your AlloyDB instance
  • Use Gemini Code Assist to add functionality to your application

2. Prerequisites

  1. If you do not already have a Google account, you must create a Google account.
    • Use a personal account instead of a work or school account. Work and school accounts may have restrictions that prevent you from enabling the APIs needed for this lab.

3. Project setup

  1. Sign-in to the Google Cloud Console.
  2. Enable billing in the Cloud Console.
    • Completing this lab should cost less than $1 USD in Cloud resources.
    • You can follow the steps at the end of this lab to delete resources to avoid further charges.
    • New users are eligible for the $300 USD Free Trial.
  3. Create a new project or choose to reuse an existing project.

4. Open Cloud Shell Editor

  1. Navigate to Cloud Shell Editor
  2. If the terminal doesn't appear on the bottom of the screen, open it:
    • Click the hamburger menu Hamburger menu icon
    • Click Terminal
    • Click New TerminalOpen new terminal in Cloud Shell Editor
  3. In the terminal, set your project with this command:
    • Format:
      gcloud config set project [PROJECT_ID]
    • Example:
      gcloud config set project lab-project-id-example
    • If you can't remember your project id:
      • You can list all your project ids with:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
      Set project id in Cloud Shell Editor terminal
  4. If prompted to authorize, click Authorize to continue. Click to authorize Cloud Shell
  5. You should see this message:
    Updated property [core/project].
    
    If you see a WARNING and are asked Do you want to continue (Y/N)?, then you have likely entered the project ID incorrectly. Press N, press Enter, and try to run the gcloud config set project command again.

5. Enable APIs

In the terminal, enable the APIs:

gcloud services enable \
  compute.googleapis.com \
  alloydb.googleapis.com \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  cloudaicompanion.googleapis.com

If prompted to authorize, click Authorize to continue. Click to authorize Cloud Shell

This command may take a few minutes to complete, but it should eventually produce a successful message similar to this one:

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

6. Set up a Service Account

Create and configure a Google Cloud service account to be used by Cloud Run so that it has the correct permissions to connect to AlloyDB.

  1. Run the gcloud iam service-accounts create command as follows to create a new service account:
    gcloud iam service-accounts create quickstart-service-account \
     
    --display-name="Quickstart Service Account"
  2. Run the gcloud projects add-iam-policy-binding command as follows to add the AlloyDB Database User role to the Google Cloud service account you just created.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/alloydb.databaseUser"
  3. Run the gcloud projects add-iam-policy-binding command as follows to add the Service Usage Consumer role to the Google Cloud service account you just created.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/serviceusage.serviceUsageConsumer"
  4. Run the gcloud projects add-iam-policy-binding command as follows to add the Log Writer role to the Google Cloud service account you just created.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/logging.logWriter"

7. Create AlloyDB Instance

  1. Run the gcloud alloydb clusters create command to create a Cloud SQL instance
    gcloud alloydb clusters create quickstart-cluster \
      --password=$(openssl rand -base64 20) \
      --region=us-central1 \
      --project=${GOOGLE_CLOUD_PROJECT} \
      --enable-private-service-connect \
      --database-version=POSTGRES_16

This command may take a few minutes to complete.

  1. Run the gcloud alloydb instances create command to create a Cloud SQL instance
    gcloud alloydb instances create quickstart-instance \
      --project=${GOOGLE_CLOUD_PROJECT} \
      --instance-type=PRIMARY \
      --cpu-count=2 \
      --region=us-central1 \
      --cluster=quickstart-cluster \
      --allowed-psc-projects=${GOOGLE_CLOUD_PROJECT} \
      --database-flags=alloydb.iam_authentication=on
  2. Run the gcloud alloydb instances describe command to get the PSC service attachment link and export it to a variable
    export SERVICE_ATTACHMENT=$(gcloud alloydb instances describe quickstart-instance \
        --cluster=quickstart-cluster --region=us-central1 \
        --format="value(pscInstanceConfig.serviceAttachmentLink)")
  3. gcloud compute addresses create quickstart-address \
      --region=us-central1 \
      --subnet=default
  4. gcloud compute forwarding-rules create quickstart-endpoint \
      --region=us-central1 \
      --network=default \
      --address=quickstart-address \
      --target-service-attachment=${SERVICE_ATTACHMENT}

Create a PostgreSQL database user for the service account you created earlier to access the database.

gcloud alloydb users create quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam \
  --cluster=quickstart-cluster \
  --region=us-central1 \
  --type=IAM_BASED \
  --superuser=true

8. Prepare Application

Prepare a Node.js application that responds to HTTP requests.

  1. In Cloud Shell create a new directory named helloworld, then change into that directory:
    mkdir helloworld
    cd helloworld
  2. Initialize a package.json file as a module.
    npm init -y
    npm pkg set type="module"
    npm pkg set main="index.mjs"
    npm pkg set scripts.start="node index.mjs"
  3. Install the Google Cloud Auth library.
    npm install google-auth-library
  4. Install pg to interact with the PostgreSQL database.
    npm install pg
  5. Install express to accept incoming http requests.
    npm install express
  6. Create an index.mjs file with the application code. This code is able to:
    • Accept HTTP requests
    • Connect to the database
    • Store the time of the HTTP request in the database
    • Return the times of the last five requests
    Run the following command in Cloud Shell:
    cat > index.mjs << "EOF"
    import express from 'express';
    import pg from 'pg';
    const { Pool } = pg;
    import {GoogleAuth} from 'google-auth-library';

    const auth = new GoogleAuth({
     
    scopes: ['https://www.googleapis.com/auth/alloydb.login'],
    });

    const pool = new Pool({
     
    host: process.env.DB_HOST,
     
    user: process.env.DB_USER,
     
    password: async () => {
       
    return await auth.getAccessToken();
     
    },
     
    database: process.env.DB_NAME,
     
    ssl: {
       
    require: true,
       
    rejectUnauthorized: false, // required for self-signed certs
       
    // https://node-postgres.com/features/ssl#self-signed-cert
     
    }
    });

    const app = express();

    app.get('/', async (req, res) => {
     
    await pool.query('INSERT INTO visits(created_at) VALUES(NOW())');
     
    const {rows} = await pool.query('SELECT created_at FROM visits ORDER BY created_at DESC LIMIT 5');
     
    console.table(rows); // prints the last 5 visits
     
    res.send(rows);
    });

    const port = parseInt(process.env.PORT) || 8080;
    app.listen(port, async () => {
     
    console.log('process.env: ', process.env);
     
    await pool.query(`CREATE TABLE IF NOT EXISTS visits (
       
    id SERIAL NOT NULL,
       
    created_at timestamp NOT NULL,
       
    PRIMARY KEY (id)
     
    );`);
     
    console.log(`helloworld: listening on port ${port}`);
    });

    EOF

This code creates a basic web server that listens on the port defined by the PORT environment variable. The application is now ready to be deployed.

9. Deploy Cloud Run Application

  1. Run the gcloud projects add-iam-policy-binding command as follows to add the Network User role to the Cloud Run service account for the Cloud Run service you are about to create.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member "serviceAccount:service-$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} --format="value(projectNumber)")@serverless-robot-prod.iam.gserviceaccount.com" \
    --role "roles/compute.networkUser"
  1. Run the command below to deploy your application to Cloud Run:
    gcloud run deploy helloworld \
      --region=us-central1 \
      --source=. \
      --set-env-vars DB_NAME="quickstart_db" \
      --set-env-vars DB_USER="postgres" \
      --set-env-vars DB_PASSWORD=${DB_PASSWORD} \
      --set-env-vars DB_HOST="$(gcloud sql instances describe quickstart-instance --project=${GOOGLE_CLOUD_PROJECT} --format='value(settings.ipConfiguration.pscConfig.pscAutoConnections.ipAddress)')" \
      --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --network=default \
      --subnet=default \
      --allow-unauthenticated
  2. If prompted, press Y and Enter to confirm that you would like to continue:
    Do you want to continue (Y/n)? Y
    

After a few minutes, the application should provide a URL for you to visit.

Navigate to the URL to see your application in action. Every time you visit the URL or refresh the page, you will see the five most recent visits returned as JSON.

After a few minutes, the application should provide a URL for you to visit.

Navigate to the URL to see your application in action. Every time you visit the URL or refresh the page, you will see the five most recent visits returned as JSON.

10. Congratulations

In this lab, you have learned how to:

  • Create an AlloyDB instance (configured to use Private Service Connect)
  • Deploy an application to Cloud Run that connects to your AlloyDB instance
  • Use Gemini Code Assist to add functionality to your application

Clean up

To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, either delete the project that contains the resources, or keep the project and delete the individual resources. If you would like to delete the entire project, you can run:

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}