Trigger Cloud Run functions using Cloud Tasks

This tutorial shows you how to use Cloud Tasks within an App Engine application to trigger a Cloud Run function and send a scheduled email.

Objectives

  • Understand the code in each of the components.
  • Create a SendGrid account.
  • Download the source code.
  • Deploy a Cloud Run function to receive Cloud Tasks requests and send an email via the SendGrid API.
  • Create a Cloud Tasks queue.
  • Create a service account to authenticate your Cloud Tasks requests.
  • Deploy the client code that allows a user to send an email.

Costs

Cloud Tasks, Cloud Run functions, and App Engine have a free tier, so as long as you are running the tutorial within the free tier of the given products it should not result in additional costs. For more information, see Pricing.

Before you begin

  1. Select or create a Google Cloud project.

    Go to the App Engine page

  2. Initialize an App Engine application in your project:

    1. On the Welcome to App Engine page click Create Application.

    2. Select a region for your application. This location will serve as the LOCATION_ID parameter for your Cloud Tasks requests, so make a note of it. Note that two locations, called europe-west and us-central in App Engine commands, are called, respectively, europe-west1 and us-central1 in Cloud Tasks commands.

    3. Select Node.js for the language and Standard for the environment.

    4. If the Enable billing popup appears, select your billing account. If you do not currently have a billing account, click Create billing account and follow the wizard.

    5. On the Get started page, click Next. You'll take care of this later.

  3. Enable the Cloud Run functions and Cloud Tasks APIs.

    Enable the apis

  4. Install and initialize the gcloud CLI.

Understanding the code

This section walks you through the app's code and explains how it works.

Creating the task

The index page is served using handlers in the app.yaml. The variables needed for task creation are passed in as environment variables.

runtime: nodejs16

env_variables:
  QUEUE_NAME: "my-queue"
  QUEUE_LOCATION: "us-central1"
  FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
  SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

# Handlers for serving the index page.
handlers:
  - url: /static
    static_dir: static
  - url: /
    static_files: index.html
    upload: index.html

This code creates the endpoint /send-email. This endpoint handles form submissions from the index page and passes that data to the task creation code.

app.post('/send-email', (req, res) => {
  // Set the task payload to the form submission.
  const {to_name, from_name, to_email, date} = req.body;
  const payload = {to_name, from_name, to_email};

  createHttpTaskWithToken(
    process.env.GOOGLE_CLOUD_PROJECT,
    QUEUE_NAME,
    QUEUE_LOCATION,
    FUNCTION_URL,
    SERVICE_ACCOUNT_EMAIL,
    payload,
    date
  );

  res.status(202).send('📫 Your postcard is in the mail! 💌');
});

This code actually creates the task and sends it on to the Cloud Tasks queue. The code builds the task by:

  • Specifying the target type as HTTP Request.

  • Specifying the HTTP method to be used and the URL of the target.

  • Setting the Content-Type header to application/json so downstream applications can parse the structured payload.

  • Adding a service account email so that Cloud Tasks can provide credentials to the request target, which requires authentication. The service account is created separately.

  • Checking to make sure the user input for date is within the 30 days maximum and adding it to the request as field scheduleTime.

const MAX_SCHEDULE_LIMIT = 30 * 60 * 60 * 24; // Represents 30 days in seconds.

const createHttpTaskWithToken = async function (
  project = 'my-project-id', // Your GCP Project id
  queue = 'my-queue', // Name of your Queue
  location = 'us-central1', // The GCP region of your queue
  url = 'https://example.com/taskhandler', // The full url path that the request will be sent to
  email = '<member>@<project-id>.iam.gserviceaccount.com', // Cloud IAM service account
  payload = 'Hello, World!', // The task HTTP request body
  date = new Date() // Intended date to schedule task
) {
  // Imports the Google Cloud Tasks library.
  const {v2beta3} = require('@google-cloud/tasks');

  // Instantiates a client.
  const client = new v2beta3.CloudTasksClient();

  // Construct the fully qualified queue name.
  const parent = client.queuePath(project, location, queue);

  // Convert message to buffer.
  const convertedPayload = JSON.stringify(payload);
  const body = Buffer.from(convertedPayload).toString('base64');

  const task = {
    httpRequest: {
      httpMethod: 'POST',
      url,
      oidcToken: {
        serviceAccountEmail: email,
        audience: url,
      },
      headers: {
        'Content-Type': 'application/json',
      },