Skip to content

feat: Add async and streaming examples #393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* [`cloud_run_http`](./cloud_run_http/) - Deploying an HTTP function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
* [`cloud_run_event`](./cloud_run_event/) - Deploying a CloudEvent function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
* [`cloud_run_cloud_events`](cloud_run_cloud_events/) - Deploying a [CloudEvent](https://github.com/cloudevents/sdk-python) function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
* [`cloud_run_async`](./cloud_run_async/) - Deploying asynchronous HTTP and CloudEvent functions to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
* [`cloud_run_streaming_http`](./cloud_run_streaming_http/) - Deploying streaming HTTP functions to [Cloud Run](http://cloud.google.com/run) with the Functions Framework

## Development Tools
* [`docker-compose`](./docker-compose) -
Expand Down
68 changes: 68 additions & 0 deletions examples/cloud_run_async/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Deploying async functions to Cloud Run

This sample shows how to deploy asynchronous functions to [Cloud Run functions](https://cloud.google.com/functions) with the Functions Framework. It includes examples for both HTTP and CloudEvent functions, which can be found in the `main.py` file.

## Dependencies

Install the dependencies for this example:

```sh
pip install -r requirements.txt
```

## Running locally

### HTTP Function

To run the HTTP function locally, use the `functions-framework` command:

```sh
functions-framework --target=hello_async_http
```

Then, send a request to it from another terminal:

```sh
curl localhost:8080
# Output: Hello, async world!
```

### CloudEvent Function

To run the CloudEvent function, specify the target and set the signature type:

```sh
functions-framework --target=hello_async_cloudevent --signature-type=cloudevent
```

Then, in another terminal, send a sample CloudEvent using the provided script:

```sh
python send_cloud_event.py
```

## Deploying to Cloud Run

You can deploy these functions to Cloud Run using the `gcloud` CLI.

### HTTP Function

```sh
gcloud run deploy async-http-function \
--source . \
--function hello_async_http \
--base-image python312 \
--region <YOUR_REGION>
```

### CloudEvent Function

```sh
gcloud run deploy async-cloudevent-function \
--source . \
--function hello_async_cloudevent \
--base-image python312 \
--region <YOUR_REGION>
```

After deploying, you can invoke the CloudEvent function by sending an HTTP POST request with a CloudEvent payload to its URL.
36 changes: 36 additions & 0 deletions examples/cloud_run_async/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import functions_framework.aio

@functions_framework.aio.http
async def hello_async_http(request):
"""
An async HTTP function.
Args:
request (starlette.requests.Request): The request object.
Returns:
The response text, or an instance of any Starlette response class
(e.g. `starlette.responses.Response`).
"""
return "Hello, async world!"

@functions_framework.aio.cloud_event
async def hello_async_cloudevent(cloud_event):
"""
An async CloudEvent function.
Args:
cloud_event (cloudevents.http.CloudEvent): The CloudEvent object.
"""
print(f"Received event with ID: {cloud_event['id']} and data {cloud_event.data}")
4 changes: 4 additions & 0 deletions examples/cloud_run_async/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
functions-framework>=3.9.2,<4.0.0

# For testing
httpx<=0.28.1
38 changes: 38 additions & 0 deletions examples/cloud_run_async/send_cloud_event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/local/bin/python

# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
import httpx

from cloudevents.http import CloudEvent, to_structured


async def main():
attributes = {
"Content-Type": "application/json",
"source": "from-galaxy-far-far-away",
"type": "cloudevent.greet.you",
}
data = {"name": "john"}

event = CloudEvent(attributes, data)

headers, data = to_structured(event)

async with httpx.AsyncClient() as client:
await client.post("http://localhost:8080/", headers=headers, data=data)

if __name__ == "__main__":
asyncio.run(main())
65 changes: 65 additions & 0 deletions examples/cloud_run_streaming_http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Deploying streaming functions to Cloud Run

This sample shows how to deploy streaming functions to [Cloud Run](http://cloud.google.com/run) with the Functions Framework. The `main.py` file contains examples for both synchronous and asynchronous streaming.

## Dependencies

Install the dependencies for this example:

```sh
pip install -r requirements.txt
```

## Running locally

### Synchronous Streaming

To run the synchronous streaming function locally:

```sh
functions-framework --target=hello_stream
```

Then, send a request to it from another terminal:

```sh
curl localhost:8080
```

### Asynchronous Streaming

To run the asynchronous streaming function locally:

```sh
functions-framework --target=hello_stream_async
```

Then, send a request to it from another terminal:

```sh
curl localhost:8080
```

## Deploying to Cloud Run

You can deploy these functions to Cloud Run using the `gcloud` CLI.

### Synchronous Streaming

```sh
gcloud run deploy streaming-function \
--source . \
--function hello_stream \
--base-image python312 \
--region <YOUR_REGION>
```

### Asynchronous Streaming

```sh
gcloud run deploy streaming-async-function \
--source . \
--function hello_stream_async \
--base-image python312 \
--region <YOUR_REGION>
```
59 changes: 59 additions & 0 deletions examples/cloud_run_streaming_http/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import time
import asyncio
import functions_framework
import functions_framework.aio
from starlette.responses import StreamingResponse

# Helper function for the synchronous streaming example.
def slow_numbers(minimum, maximum):
yield '<html><body><ul>'
for number in range(minimum, maximum + 1):
yield '<li>%d</li>' % number
time.sleep(0.5)
yield '</ul></body></html>'

@functions_framework.http
def hello_stream(request):
"""
A synchronous HTTP function that streams a response.
Args:
request (flask.Request): The request object.
Returns:
A generator, which will be streamed as the response.
"""
generator = slow_numbers(1, 10)
return generator, {'Content-Type': 'text/html'}

# Helper function for the asynchronous streaming example.
async def slow_numbers_async(minimum, maximum):
yield '<html><body><ul>'
for number in range(minimum, maximum + 1):
yield '<li>%d</li>' % number
await asyncio.sleep(0.5)
yield '</ul></body></html>'

@functions_framework.aio.http
async def hello_stream_async(request):
"""
An asynchronous HTTP function that streams a response.
Args:
request (starlette.requests.Request): The request object.
Returns:
A starlette.responses.StreamingResponse.
"""
generator = slow_numbers_async(1, 10)
return StreamingResponse(generator, media_type='text/html')
1 change: 1 addition & 0 deletions examples/cloud_run_streaming_http/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
functions-framework>=3.9.2,<4.0.0
Loading