Writing Cloud Functions

Cloud Functions can be written in Node.js, Python, Go, Java, .NET, and Ruby programming languages, and are executed in language-specific runtimes. The Cloud Functions execution environment varies by your chosen runtime. The runtime overview pages provide further details about each runtime environment:

Try it for yourself

If you're new to Google Cloud, create an account to evaluate how Cloud Functions performs in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.

Try Cloud Functions free

Types of Cloud Functions

There are two distinct types of Cloud Functions: HTTP functions and event-driven functions. Event-driven functions can be either background functions or CloudEvent functions, depending on which Cloud Functions runtime they are written for.

HTTP functions

You invoke HTTP functions from standard HTTP requests. These HTTP requests wait for the response and support handling of common HTTP request methods like GET, PUT, POST, DELETE and OPTIONS. When you use Cloud Functions, a TLS certificate is automatically provisioned for you, so all HTTP functions can be invoked via a secure connection.

For details, see Writing HTTP Functions.

Example:

Node.js

const escapeHtml = require('escape-html');

/**
 * HTTP Cloud Function.
 *
 * @param {Object} req Cloud Function request context.
 *                     More info: https://expressjs.com/en/api.html#req
 * @param {Object} res Cloud Function response context.
 *                     More info: https://expressjs.com/en/api.html#res
 */
exports.helloHttp = (req, res) => {
  res.send(`Hello ${escapeHtml(req.query.name || req.body.name || 'World')}!`);
};

Python

from flask import escape

def hello_http(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <https://flask.palletsprojects.com/en/1.1.x/api/#incoming-request-data>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response>.
    """
    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and 'name' in request_json:
        name = request_json['name']
    elif request_args and 'name' in request_args:
        name = request_args['name']
    else:
        name = 'World'
    return 'Hello {}!'.format(escape(name))

Go


// Package helloworld provides a set of Cloud Functions samples.
package helloworld

import (
	"encoding/json"
	"fmt"
	"html"
	"net/http"
)

// HelloHTTP is an HTTP Cloud Function with a request parameter.
func HelloHTTP(w http.ResponseWriter, r *http.Request) {
	var d struct {
		Name string `json:"name"`
	}
	if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	if d.Name == "" {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	fmt.Fprintf(w, "Hello, %s!", html.EscapeString(d.Name))
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Logger;

public class HelloHttp implements HttpFunction {
  private static final Logger logger = Logger.getLogger(HelloHttp.class.getName());

  private static final Gson gson = new Gson();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Check URL parameters for "name" field
    // "world" is the default value
    String name = request.getFirstQueryParameter("name").orElse("world");

    // Parse JSON request and check for "name" field
    try {
      JsonElement requestParsed = gson.fromJson(request.getReader(), JsonElement.class);
      JsonObject requestJson = null;

      if (requestParsed != null && requestParsed.isJsonObject()) {
        requestJson = requestParsed.getAsJsonObject();
      }

      if (requestJson != null && requestJson.has("name")) {
        name = requestJson.get("name").getAsString();
      }
    } catch (JsonParseException e) {
      logger.severe("Error parsing JSON: " + e.getMessage());
    }

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Hello %s!", name);
  }
}

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;

namespace HelloHttp
{
    public class Function : IHttpFunction
    {
        private readonly ILogger _logger;

        public Function(ILogger<Function> logger) =>
            _logger = logger;

        public async Task HandleAsync(HttpContext context)
        {
            HttpRequest request = context.Request;
            // Check URL parameters for "name" field
            // "world" is the default value
            string name = ((string) request.Query["name"]) ?? "world";

            // If there's a body, parse it as JSON and check for "name" field.
            using TextReader reader = new StreamReader(request.Body);
            string text = await reader.ReadToEndAsync();
            if (text.Length > 0)
            {
                try
                {
                    JsonElement json = JsonSerializer.Deserialize<JsonElement>(text);
                    if (json.TryGetProperty("name", out JsonElement nameElement) &&
                        nameElement.ValueKind == JsonValueKind.String)
                    {
                        name = nameElement.GetString();
                    }
                }
                catch (JsonException parseException)
                {
                    _logger.LogError(parseException, "Error parsing JSON request");
                }
            }

            await context.Response.WriteAsync($"Hello {name}!");
        }
    }
}

Ruby

require "functions_framework"
require "cgi"
require "json"

FunctionsFramework.http "hello_http" do |request|
  # The request parameter is a Rack::Request object.
  # See https://www.rubydoc.info/gems/rack/Rack/Request
  name = request.params["name"] ||
         (JSON.parse(request.body.read)["name"] rescue nil) ||
         "World"
  # Return the response body as a string.
  # You can also return a Rack::Response object, a Rack response array, or
  # a hash which will be JSON-encoded into a response.
  "Hello #{CGI.escape_html name}!"
end

Event-driven functions

Cloud Functions uses event-driven functions to handle events from your Cloud infrastructure, such as messages on a Pub/Sub topic, or changes in a Cloud Storage bucket.

Cloud Functions supports two sub-types of event-driven functions:

As explained below, the sub-type you use will be governed by the runtime your function targets.

Background functions

Event-driven functions written for the Node.js, Python, Go, and Java Cloud Functions runtimes are known as background functions. See Writing Background Functions for more details.

Example:

Node.js

/**
 * Background Cloud Function to be triggered by Pub/Sub.
 * This function is exported by index.js, and executed when
 * the trigger topic receives a message.
 *
 * @param {object} message The Pub/Sub message.
 * @param {object} context The event metadata.
 */
exports.helloPubSub = (message, context) => {
  const name = message.data
    ? Buffer.from(message.data, 'base64').toString()
    : 'World';

  console.log(`Hello, ${name}!`);
};

Python

def hello_pubsub(event, context):
    """Background Cloud Function to be triggered by Pub/Sub.
    Args:
         event (dict):  The dictionary with data specific to this type of
         event. The `data` field contains the PubsubMessage message. The
         `attributes` field will contain custom attributes if there are any.
         context (google.cloud.functions.Context): The Cloud Functions event
         metadata. The `event_id` field contains the Pub/Sub message ID. The
         `timestamp` field contains the publish time.
    """
    import base64

    print("""This Function was triggered by messageId {} published at {}
    """.format(context.event_id, context.timestamp))

    if 'data' in event:
        name = base64.b64decode(event['data']).decode('utf-8')
    else:
        name = 'World'
    print('Hello {}!'.format(name))

Go


// Package helloworld provides a set of Cloud Functions samples.
package helloworld

import (
	"context"
	"log"
)

// PubSubMessage is the payload of a Pub/Sub event.
// See the documentation for more details:
// https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage
type PubSubMessage struct {
	Data []byte `json:"data"`
}

// HelloPubSub consumes a Pub/Sub message.
func HelloPubSub(ctx context.Context, m PubSubMessage) error {
	name := string(m.Data) // Automatically decoded from base64.
	if name == "" {
		name = "World"
	}
	log.Printf("Hello, %s!", name)
	return nil
}

Java


import com.google.cloud.functions.BackgroundFunction;
import com.google.cloud.functions.Context;
import com.google.events.cloud.pubsub.v1.Message;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;

public class HelloPubSub implements BackgroundFunction<Message> {
  private static final Logger logger = Logger.getLogger(HelloPubSub.class.getName());

  @Override
  public void accept(Message message, Context context) {
    String name = "world";
    if (message != null && message.getData() != null) {
      name = new String(
          Base64.getDecoder().decode(message.getData().getBytes(StandardCharsets.UTF_8)),
          StandardCharsets.UTF_8);
    }
    logger.info(String.format("Hello %s!", name));
    return;
  }
}

CloudEvent functions

Event-driven functions written for the .NET Cloud Functions runtime are known as CloudEvent functions. See Writing CloudEvent Functions for more details.

Example:

C#

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Cloud.PubSub.V1;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace HelloPubSub
{
    public class Function : ICloudEventFunction<MessagePublishedData>
    {
        private readonly ILogger _logger;

        public Function(ILogger<Function> logger) =>
            _logger = logger;

        public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken)
        {
            string nameFromMessage = data.Message?.TextData;
            string name = string.IsNullOrEmpty(nameFromMessage) ? "world" : nameFromMessage;
            _logger.LogInformation("Hello {name}", name);
            return Task.CompletedTask;
        }
    }
}

Ruby

require "functions_framework"
require "base64"

FunctionsFramework.cloud_event "hello_pubsub" do |event|
  # The event parameter is a CloudEvents::Event::V1 object.
  # See https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1.html
  name = Base64.decode64 event.data["message"]["data"] rescue "World"

  # A cloud_event function does not return a response, but you can log messages
  # or cause side effects such as sending additional events.
  logger.info "Hello, #{name}!"
end

Structuring source code

In order for Cloud Functions to find your function's definition, each runtime has structuring requirements for your source code. In general, we recommend splitting up large, multi-function codebases into smaller per-function ones to reduce code complexity and per-function dependency counts.

Node.js

For the Node.js runtimes, your function's source code must be exported from a Node.js module, which Cloud Functions loads using a require() call. In order to determine which module to load, Cloud Functions uses the main field in your package.json file. If the main field is not specified, Cloud Functions loads code from index.js.

For example, the following configurations of source code are valid:

  • A single index.js located in your function's root directory that exports one or more functions:

    .
    └── index.js
    
  • An index.js file that imports code from a foo.js file and then exports one or more functions:

    .
    ├── index.js
    └── foo.js
    
  • An app.js file that exports one or more functions, with a package.json file that contains "main": "app.js":

    .
    ├── app.js
    └── package.json
    

Python

For the Python runtime, your function's entrypoint must be defined in a Python source file named main.py located in the functions's root directory.

For example, the following configurations of source code are valid:

  • A single main.py file in your function's root directory that defines one or more functions:

    .
    └── main.py
    
  • A main.py file with a requirements.txt file that specifies dependencies:

    .
    ├── main.py
    └── requirements.txt
    
  • A main.py file that imports code from a local dependency:

    .
    ├── main.py
    └── mylocalpackage/
        ├── __init__.py
        └── myscript.py
    

Go

For the Go runtime, your function must be in a Go package at the root of your project. Your function cannot be in package main. Sub-packages are only supported when using Go modules.

For example, the following configurations of source code are valid:

  • A package at the root of your project that exports one or more functions:

    .
    └── function.go
    
  • A package at the root of your project that imports code from a sub-package and exports one or more functions:

    .
    ├── function.go
    ├── go.mod
    └── shared/
        └── shared.go
    
  • A package at the root of your project with a sub-directory that defines a package main:

    .
    ├── cmd/
    |   └── main.go
    └── function.go
    

Java

For the Java runtime, you should create a top-level function directory containing a src/main/java/ sub-directory and a pom.xml file. We recommend putting tests in a src/test/java/ sub-directory.

.
├── pom.xml
└── src/
    ├── main/
    |   └── java/
    |       └── MyFunction.java
    └── test
        └── java/
            └── MyFunctionTest.java

If your .java file declares a package (for example, functions), your directory hierarchy would look like this:

.
├── pom.xml
└── src/
    ├── main/
    |   └── java/
    |       └── functions/
    |               └── MyFunction.java
    └── test/
        └── java/
                └── functions/
                    └── MyFunctionTest.java

If your function is defined in a specific package as most Java functions are, it must be specified as part of your --entry-point value at deployment time.

Grouping multiple functions

If you are thinking of grouping multiple functions into a single project, be aware that every function may end up sharing the same set of dependencies. But some of the functions may not need all of the shared dependencies.

We recommend putting each function in its own top-level directory as shown above, with its own src/main/java sub-directory and pom.xml file. This approach minimizes the number of dependencies required for a particular function, which in turn reduces the amount of memory your function needs.

Furthermore, separate functions make it easier to specify a single function when running functions locally via the Functions Framework. This can be helpful for local development and testing.

C#

For the .NET runtime, you can structure your projects as you would any other .NET source code.

When you use the templates to create a C# function, it creates the following files at the same level in the file system:

  • A function source file named Function.cs.
  • A project file with a .csproj extension.

For example:

.
├── Function.cs
└── HelloHttp.csproj

The same pattern applies when you use the templates to create F# and Visual Basic functions. For F#, the file name is Function.fs, and the project file has the extension .fsproj. For Visual Basic, the file name is CloudFunction.vb, and the project file has the extension .vbproj.

However, these patterns are just conventions, not requirements. You can structure your code as you would any other regular C#, F#, or Visual Basic project, which can contain multiple source files, resources and so on.

Additionally, if you only implement a single function per project, you can start hosting that function without specifying the function name, which can make local development and debugging simpler.

Ruby

For the Ruby runtime, your function's entrypoint must be defined in a Ruby source file named app.rb located in the function's root directory. Additionally, dependencies, including at least the functions_framework gem, must be listed in a Gemfile and associated Gemfile.lock file also located in the function's root directory. Your function can include additional Ruby files in the root directory or subdirectories.

For example, the following configurations of source code are valid:

.
├── Gemfile
├── Gemfile.lock
└── app.rb

.
├── Gemfile
├── Gemfile.lock
├── app.rb
└── lib/
    └── my_class.rb

Specifying dependencies

You specify your function's dependencies idiomatically based on the runtime you are using. For more details, see the appropriate page:

Naming Cloud Functions

Cloud Functions have a "name" property that is set at deploy time, and once set, it cannot be changed. The name of a function is used as its identifier and it must be unique within a region. See the deployment documentation for details.

Next steps

Try it for yourself

If you're new to Google Cloud, create an account to evaluate how Cloud Functions performs in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.

Try Cloud Functions free