Writing Cloud Functions

Cloud Functions can be written in Node.js, Python, Go, and Java, 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:

Types of Cloud Functions

There are two distinct types of Cloud Functions: HTTP functions and background functions.

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.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.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);
  }
}

Background functions

You can use background functions to handle events from your Cloud infrastructure, such as messages on a Pub/Sub topic, or changes in a Cloud Storage bucket.

For details, see Writing Background Functions.

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} data The event payload.
 * @param {object} context The event metadata.
 */
exports.helloPubSub = (data, context) => {
  const pubSubMessage = data;
  const name = pubSubMessage.data
    ? Buffer.from(pubSubMessage.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.
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 functions.eventpojos.PubSubMessage;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;

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

  @Override
  public void accept(PubSubMessage 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;
  }
}

Structuring source code

In order for Cloud Functions to find your function's definition, each runtime has structuring requirements for your source code:

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 file at the root of your project 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 at the root of your project named main.py.

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

  • A single main.py file at the root of your project 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.

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