This page describes how to write HTTP and event-driven Cloud Run functions with the Functions Framework.
Functions Framework overview
When writing functions source code, you must use the Functions Framework, an open source library for writing Cloud Run functions. With the Functions Framework, you can write lightweight functions that run in Cloud Run and in other environments, including your local development machine and Knative-based environments.
The Functions Framework lets you:
- Invoke a Cloud Run function in response to a request.
- Automatically unmarshal events conforming to the CloudEvents specification, an industry-standard specification for describing event data in a common way.
- Start a local development server for testing.
The Functions Framework provides an interface for building modular services. To use the Functions Framework in your source code, specify the following:
Function entry point
Your source code must define a function entry point, which is the code that runs when Cloud Run invokes your function. You specify this entry point when you deploy your function.
How you define the entry point depends on the language runtime you use. Some languages use a function as their entry point, while others use a class.
Signature Type
When you write the source code of a function with the Functions Framework, you must specify one of the two signature types:
- HTTP functions: Registers an HTTP handler function. Use an HTTP function when your function needs a URL endpoint and must respond to HTTP requests, such as for webhooks.
- Event-driven functions, also known as CloudEvents functions: Registers a CloudEvents handler function. Use an event-driven function when your function is triggered directly in response to events within your Google Cloud project, such as messages on a Pub/Sub topic or changes in a Cloud Storage bucket.
Source directory structure
The Functions Framework is supported in a number of programming languages. The language runtime you choose and the type of function you want to write determines how to structure your code and implement your function.
For Cloud Run to locate your function definition, each language runtime has requirements for structuring your source code.
Node.js
The basic directory structure for Node.js functions is as follows:
. ├── index.js └── package.json
By default, Cloud Run attempts to load source code from a file
named index.js
at the root of your function directory. To specify a different
main source file, use the main
field in your package.json
file.
Your package.json
file must also include the
Functions Framework for Node.js
as a dependency:
{
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
},
"type": "module"
}
The code in your main file must define your function entry point and can import other code and Node.js modules. The main file can also define multiple function entry points that can be deployed separately.
See the Node.js runtime overview and the Functions Framework for Node.js for more details.
Python
The basic directory structure for Python functions is as follows:
. ├── main.py └── requirements.txt
Cloud Run loads source code from a file named main.py
at the
root of your function directory. You must name your main file main.py
.
Your requirements.txt
file must include the
Functions Framework for Python
as a dependency:
functions-framework==3.*
The code in your main.py
file must define your
function entry point and can import other code and external
dependencies as normal. The main.py
file can also define multiple function
entry points that can be deployed separately.
See the Python runtime overview and the Functions Framework for Python for more details.
Go
The basic directory structure for Go functions is as follows:
. ├── myfunction.go └── go.mod
Your function must be in a Go package at the root of your project. The package
and its source files can have any name, except your function cannot be in
package main
. If you need a main
package, for example for local testing,
you can create one in a subdirectory:
. ├── myfunction.go ├── go.mod └── cmd/ └── main.go
Your go.mod
file must include the
Functions Framework for Go
as a dependency:
module example.com/my-module
require (
github.com/GoogleCloudPlatform/functions-framework-go v1.5.2
)
The code in your root package must define your function entry point and can import other code from subpackages and dependencies as normal. Your package can also define multiple function entry points that can be deployed separately.
See the Go runtime overview and the Functions Framework for Go for more details.
Java
The basic directory structure for Java functions is as follows:
. ├── pom.xml └── src/ └── main/ └── java/ └── MyFunction.java
Your Java source files must be under the src/main/java/
directory and can have
any name. If your source files declare a package, add an extra directory under
src/main/java
with the name of the package:
. ├── pom.xml └── src/ └── main/ └── java/ └── mypackage/ └── MyFunction.java
We recommend putting associated tests under a src/test/java/
subdirectory.
Your pom.xml
file must include the
Functions Framework for Java
as a dependency:
...
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.0.4</version>
</dependency>
...
The code in your source files must define your function entry point and can import other code and external dependencies as normal. Your source files can also define multiple function entry points that can be deployed separately.
See the Java runtime overview and the Functions Framework for Java for more details.
.NET
The basic directory structure for .NET functions is as follows:
. ├── MyFunction.cs └── MyProject.csproj
You can structure your projects as you would any other .NET source code. Your source files can have any name.
Your project file must include the Functions Framework for .NET as a dependency:
...
<PackageReference Include="Google.Cloud.Functions.Hosting" Version="1.0.0" />
...
The code in your source files must define your function entry point and can import other code and external dependencies as normal. Your source files can also define multiple function entry points that can be deployed separately.
See the .NET runtime overview and the Functions Framework for .NET for more details.
Ruby
The basic directory structure for Ruby functions is as follows:
. ├── app.rb ├── Gemfile └── Gemfile.lock
Cloud Run loads source code from a file named app.rb
at the
root of your function directory. Your main file must be named app.rb
.
Your Gemfile
file must include the
Functions Framework for Ruby
as a dependency:
source "https://rubygems.org"
gem "functions_framework", "~> 1.0"
The code in your app.rb
file must define your
function entry point and can import other code and external
dependencies as normal. The app.rb
file can also define multiple function
entry points that can be deployed separately.
See the Ruby runtime overview and the Functions Framework for Ruby for more details.
PHP
The basic directory structure for PHP functions is as follows:
. ├── index.php └── composer.json
Cloud Run loads source code from a file named index.php
at the
root of your function directory. You must name your main file index.php
.
Your composer.json
file must include the
Functions Framework for PHP
as a dependency:
{
"require": {
"google/cloud-functions-framework": "^1.1"
}
}
The code in your index.php
file must define your
function entry point and can import other code and
external dependencies as normal. The index.php
file can also define multiple
function entry points that can be deployed separately.
See the PHP runtime overview and the Functions Framework for PHP for more details.
If you group multiple functions into a single project, be aware that every function might end up sharing the same set of dependencies. However, some of the functions might not need all of the dependencies.
Where possible, we recommend splitting up large multi-function codebases and putting each function in its own top-level directory as shown in the preceding examples, with its own source and project configuration files. This approach minimizes the number of dependencies required for a particular function, which in turn reduces the amount of memory your function needs.
Write HTTP functions
Write an HTTP function when you want to invoke a function through an HTTP(S) request. To allow for HTTP semantics, you use the Function Framework and specify the HTTP Function signature to accept HTTP-specific arguments.
The following example shows a basic HTTP function source file for each runtime. For a complete working example, see Deploy a Cloud Run function using the Google Cloud CLI. For more information about where to locate your source code, see Source directory structure.
Node.js
ES module
import { http } from '@google-cloud/functions-framework';
http('myHttpFunction', (req, res) => {
// Your code here
// Send an HTTP response
res.send('OK');
});
Add the following dependencies, including "type": "module"
in your
package.json
file:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
},
"type": "module"
}
CommonJS module
const functions = require('@google-cloud/functions-framework');
// Register an HTTP function with the Functions Framework
functions.http('myHttpFunction', (req, res) => {
// Your code here
// Send an HTTP response
res.send('OK');
});
Add the following dependencies in your package.json
file:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
}
}
In Node.js, you register an HTTP handler function with the Functions Framework for Node.js. Your HTTP handler function must be an Express middleware function that accepts the request and response arguments and sends an HTTP response.
Cloud Run automatically parses the request body for you based on the
request's Content-Type
header using
body-parser
,
so you can access the req.body
and req.rawBody
objects in your HTTP handler.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is myHttpFunction
.
Python
import functions_framework
# Register an HTTP function with the Functions Framework
@functions_framework.http
def my_http_function(request):
# Your code here
# Return an HTTP response
return 'OK'
In Python, you register an HTTP handler function with the Functions Framework for Python. Your HTTP handler function must accept a Flask request object as an argument and return a value that Flask can convert into an HTTP response object.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is my_http_function
.
Go
package myhttpfunction
import (
"fmt"
"net/http"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
)
func init() {
// Register an HTTP function with the Functions Framework
functions.HTTP("MyHTTPFunction", myHTTPFunction)
}
// Function myHTTPFunction is an HTTP handler
func myHTTPFunction(w http.ResponseWriter, r *http.Request) {
// Your code here
// Send an HTTP response
fmt.Fprintln(w, "OK")
}
In Go, you register an HTTP handler function with the
Functions Framework for Go
in your init()
function. Your HTTP handler function must use the standard
http.HandlerFunc
interface to send an HTTP response.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is MyHTTPFunction
.
Your HTTP handler function must implement the standard
http.HandlerFunc
interface. It accepts an http.ResponseWriter interface that your function uses
to create a reply to the request, and a pointer to an
http.Request struct containing the
details of the inbound HTTP request.
Java
package myhttpfunction;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
// Define a class that implements the HttpFunction interface
public class MyHttpFunction implements HttpFunction {
// Implement the service() method to handle HTTP requests
@Override
public void service(HttpRequest request, HttpResponse response) throws Exception {
// Your code here
// Send an HTTP response
response.getWriter().write("OK");
}
}
In Java, you use the
Functions Framework Java API
to implement an HTTP handler class with the
HttpFunction
interface. The service()
method must send an HTTP response.
The
function entry point
is the fully-qualified name of the HTTP handler class, including the package
name. In this example, the entry point is myhttpfunction.MyHttpFunction
.
Your service
method receives an
HttpRequest
object describing the inbound HTTP request, and an
HttpResponse
object that your function populates with a response message.
.NET
using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace MyProject
{
// Define a class that implements the IHttpFunction interface
public class MyHttpFunction : IHttpFunction
{
// Implement the HandleAsync() method to handle HTTP requests
public async Task HandleAsync(HttpContext context)
{
// Your code here
// Send an HTTP response
await context.Response.WriteAsync("OK");
}
}
}
In .NET runtimes, you use the
Functions Framework for .NET
to implement an HTTP handler class with the
IHttpFunction
interface. The HandleAsync()
method accepts a standard ASP.NET
HttpContext
object as an argument and must send an HTTP response.
The function entry point
is the fully-qualified name of the HTTP handler class, including the
namespace. In this example, the entry point is MyProject.MyHttpFunction
.
Ruby
require "functions_framework"
# Register an HTTP function with the Functions Framework
FunctionsFramework.http "my_http_function" do |request|
# Your code here
# Return an HTTP response
"OK"
end
In Ruby, you register an HTTP handler function with the Functions Framework for Ruby. Your HTTP handler function must accept a Rack request object as an argument and return a value that can be used as an HTTP response.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is my_http_function
.
PHP
<?php
use Google\CloudFunctions\FunctionsFramework;
use Psr\Http\Message\ServerRequestInterface;
// Register an HTTP function with the Functions Framework
FunctionsFramework::http('myHttpFunction', 'myHttpHandler');
// Define your HTTP handler
function myHttpHandler(ServerRequestInterface $request): string
{
// Your code here
// Return an HTTP response
return 'OK';
}
In PHP, you register an HTTP handler function with the
Functions Framework for PHP.
Your HTTP handler function must accept an argument that implements the PSR-7
ServerRequestInterface
interface, and must return an HTTP response as either a string or an object
that implements the PSR-7
ResponseInterface
interface.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is myHttpFunction
.
HTTP request and responses
When you register an HTTP handler function with the Functions Framework, your HTTP handler can inspect the request method and perform different actions based on the method.
When you configure an event provider to send HTTP requests to your Cloud Run function, your function sends an HTTP response. If the function creates background tasks (such as with threads, futures, JavaScript Promise objects, callbacks, or system processes), you must terminate or otherwise resolve these tasks before sending an HTTP response. Any tasks not terminated before the HTTP response is sent might not be completed, and might cause undefined behavior.
Handle CORS
Cross-Origin Resource Sharing (CORS) is a way to let applications running on one domain access resources from another domain. For example, you might need to let your domain make requests to the Cloud Run functions domain to access your function.
To allow cross-origin requests to your function, set the
Access-Control-Allow-Origin
header as appropriate on your HTTP response. For
preflighted cross-origin requests,
you must respond to the preflight OPTIONS
request with a 204
response code
and additional headers.
Node.js
Python
Go
Java
.NET
Ruby
PHP
If CORS is not set up properly, you might see errors like the following:
XMLHttpRequest cannot load https://YOUR_FUNCTION_URL. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://YOUR_DOMAIN' is therefore not allowed access.
CORS limitations
For preflighted cross-origin requests, preflight OPTIONS requests are sent without an Authorization header, so they will be rejected on all HTTP functions that require authentication. Because the preflight requests fail, the main requests will also fail. To work around this limitation, use one of the following options:
- Allow public access of your function.
- Configure Identity-Aware Proxy for your function, which provides the Authorization header for preflight OPTIONS requests sent to Cloud Run functions.
- Host your web app and Cloud Run on the same domain to avoid CORS. You can do this by integrating Firebase Hosting with Cloud Run functions.
Write event-driven functions
Write an event-driven function when you want a function to be triggered directly in response to events within your Google Cloud project, such as messages on a Pub/Sub topic or changes in a Cloud Storage bucket.
Event-driven functions are based on CloudEvents, an industry-standard specification for describing event data in a common way. You can learn more about the CloudEvents specification at the CloudEvents GitHub repository. The CloudEvents project also provides a set of CloudEvents SDKs to help work with CloudEvents objects in your code.
The following example shows an event-driven function source file for each runtime. See Source directory structure for information about where to locate your source code.
Node.js
ES module
import { cloudEvent } from "@google-cloud/functions-framework";
cloudEvent('myCloudEventFunction', cloudEvent => {
// Your code here
// Access the CloudEvent data payload using cloudEvent.data
});
Add the following dependencies, including "type": "module"
in your
package.json
file:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
},
"type": "module"
}
CommonJS module
const functions = require('@google-cloud/functions-framework');
// Register a CloudEvent function with the Functions Framework
functions.cloudEvent('myCloudEventFunction', cloudEvent => {
// Your code here
// Access the CloudEvent data payload using cloudEvent.data
});
Add the following dependencies in your package.json
file:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
}
}
In Node.js, you register a CloudEvent handler function with the
Functions Framework for Node.js.
Your handler function must accept a
CloudEvent
object as an argument.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is myCloudEventFunction
.
Python
import functions_framework
# Register a CloudEvent function with the Functions Framework
@functions_framework.cloud_event
def my_cloudevent_function(cloud_event):
# Your code here
# Access the CloudEvent data payload via cloud_event.data
In Python, you register a CloudEvent handler function with the
Functions Framework for Python.
Your handler function must accept a
CloudEvent
object as an argument.
The
function entry point
is the name of the handler function registered with the Functions Framework.
In this example, the entry point is my_cloudevent_function
.
Go
package mycloudeventfunction
import (
"context"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"github.com/cloudevents/sdk-go/v2/event"
)
func init() {
// Register a CloudEvent function with the Functions Framework
functions.CloudEvent("MyCloudEventFunction", myCloudEventFunction)
}
// Function myCloudEventFunction accepts and handles a CloudEvent object
func myCloudEventFunction(ctx context.Context, e event.Event) error {
// Your code here
// Access the CloudEvent data payload using e.Data() or e.DataAs(...)
// Returning an error causes its message to be logged.
// Example:
err := myInternalFunction() // may return an error
if err != nil {
// Append error message to log
return err
}
// Return nil if no error occurred
return nil
}
In Go, you register a CloudEvent handler function with the
Functions Framework for Go.
Your handler function must accept a CloudEvents
event.Event
object as an argument.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is MyCloudEventFunction
.
Java
package mycloudeventfunction;
import com.google.cloud.functions.CloudEventsFunction;
import io.cloudevents.CloudEvent;
// Define a class that implements the CloudEventsFunction interface
public class MyCloudEventFunction implements CloudEventsFunction {
// Implement the accept() method to handle CloudEvents
@Override
public void accept(CloudEvent event) {
// Your code here
// Access the CloudEvent data payload using event.getData()
// To get the data payload as a JSON string, use:
// new String(event.getData().toBytes())
}
}
In Java, you use the
Functions Framework Java API
to implement a CloudEvent handler class with the
CloudEventsFunction
interface. The accept()
method must accept a
CloudEvent
object as an argument and perform any processing on the event.
The
function entry point
is the fully-qualified name of the CloudEvent handler class, including the
package name. In this example, the entry point is
mycloudeventfunction.MyCloudEventFunction
.
.NET
using CloudNative.CloudEvents; using Google.Cloud.Functions.Framework; using System.Threading; using System.Threading.Tasks; namespace MyProject { // Define a class that implements the ICloudEventFunction<T> interface public class MyCloudEventFunction : ICloudEventFunction<CloudEventDataType> { // Implement the HandleAsync() method to handle CloudEvents public Task HandleAsync(CloudEvent cloudEvent, CloudEventDataType data, CancellationToken cancellationToken) { // Your code here // The data argument represents the CloudEvent data payload // Signal function completion return Task.CompletedTask; } } }
In .NET runtimes, you use the
Functions Framework for .NET
to implement a CloudEvent handler class with the
ICloudEventFunction<T>
interface. The HandleAsync()
method accepts a
CloudEvent
object and the associated CloudEvent data payload as arguments.
The type of the CloudEvent data payload argument, shown in the example code as
CloudEventDataType
, must correspond to the type of event
the function handles. The
Google CloudEvents .NET library
provides data types for the various events supported by Google.
The
function entry point
is the fully-qualified name of the CloudEvent handler class, including the
namespace. In this example, the entry point is MyProject.MyCloudEventFunction
.
Ruby
require "functions_framework"
# Register a CloudEvent function with the Functions Framework
FunctionsFramework.cloud_event "my_cloudevent_function" do |cloud_event|
# Your code here
# Access the CloudEvent data payload via cloud_event.data
end
In Ruby, you register a CloudEvent handler function with the
Functions Framework for Ruby.
Your handler function must accept a CloudEvents
Event
object as an argument.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is my_cloudevent_function
.
PHP
<?php
use CloudEvents\V1\CloudEventInterface;
use Google\CloudFunctions\FunctionsFramework;
// Register a CloudEvent function with the Functions Framework
FunctionsFramework::cloudEvent('myCloudEventFunction', 'myCloudEventHandler');
// Define your CloudEvent handler
function myCloudEventHandler(CloudEventInterface $event): void
{
// Your code here
// Access the CloudEvent data payload using $event->getData()
}
In PHP, you register a CloudEvent handler function with the
Functions Framework for PHP.
Your handler function must accept an argument that conforms to the
CloudEventInterface
interface.
The
function entry point
is the name with which the handler is registered with the Functions Framework.
In this example, the entry point is myCloudEventFunction
.
For event-driven functions, event data is passed to your function in the CloudEvents format, with a CloudEvent data payload corresponding to the event type that triggers your function. See Function triggers for information about supported triggers, event types, and associated event data formats.
The Google Events repository contains resources for working with CloudEvents issued by Google.
Function termination
Cloud Run considers event-driven function execution complete when the function returns. If the function creates background tasks (such as with threads, futures, JavaScript Promise objects, callbacks, or system processes), you must terminate or otherwise resolve these tasks before returning from your function. Any tasks not terminated before the function returns might not be completed, and might cause undefined behavior.
Automatic retries
Event-driven functions can be configured to automatically retry failed invocations. See Retrying event-driven functions for more information.
What's next
- Function triggers
- Deploy a Cloud Run function
- Learn about the Functions Framework Contract