Skip to content
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
8 changes: 8 additions & 0 deletions src/content/docs/workers/framework-guides/apis/flask.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
pcx_content_type: navigation
Comment thread
hoodmane marked this conversation as resolved.
title: Flask
description: Deploy Flask applications on Cloudflare Workers with Python support.
external_link: /workers/languages/python/packages/flask/
products:
- workers
---
40 changes: 30 additions & 10 deletions src/content/docs/workers/languages/python/packages/fastapi.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,23 @@ products:

import { Render, WranglerConfig } from "~/components";

The FastAPI package is supported in Python Workers.
[FastAPI](https://fastapi.tiangolo.com/) is supported in Python Workers.

FastAPI applications use a protocol called the [Asynchronous Server Gateway Interface (ASGI)](https://asgi.readthedocs.io/en/latest/).
This means that FastAPI never reads from or writes to a socket itself. An ASGI application expects to be hooked up to an ASGI server,
typically [uvicorn](https://uvicorn.dev/).
The ASGI server handles all of the raw sockets on the application’s behalf.

The Python Workers provide [an ASGI server](https://github.com/cloudflare/workers-py/blob/main/packages/runtime-sdk/src/asgi.py)
The Python Workers provide [an ASGI server](https://github.com/cloudflare/workers-py/blob/main/packages/runtime-sdk/src/workers/asgi.py)
that you can use directly in your Python Worker, which lets you use FastAPI in Python Workers.

## Quick Start

To get started with FastAPI in Python Workers, follow these steps:

2. Create a `src/main.py` file with your FastAPI application:
<Steps>

1. Create a `src/main.py` file with your FastAPI application:
```python
from fastapi import FastAPI

Expand All @@ -39,12 +41,13 @@ from workers import asgi
Default = asgi.entrypoint(app)
```

3. Create a `wrangler.jsonc` file to configure your Worker:
2. Create a `wrangler.jsonc` file to configure your Worker:

<WranglerConfig>

```jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-fastapi-app",
"main": "src/main.py",
"compatibility_date": "$today",
Expand All @@ -53,7 +56,7 @@ Default = asgi.entrypoint(app)
```
</WranglerConfig>

4. Create a `pyproject.toml` file to manage your dependencies:
3. Create a `pyproject.toml` file to manage your dependencies:
```toml
[project]
name = "my-fastapi-app"
Expand All @@ -70,18 +73,36 @@ dev = [
]
```

5. Run your Worker locally:
4. Run your Worker locally:
```bash
uv run pywrangler dev
```

5. In another terminal, send a request to the Worker:

```sh
curl http://localhost:8787/
```

The Worker returns:

```json output
{"Hello": "World"}
```
</Steps>


## Serve a frontend

You can serve a single-page application (SPA) or any static frontend alongside your FastAPI backend by using [Workers Static Assets](/workers/static-assets/).
You can serve any static frontend alongside your FastAPI backend by using [Workers Static Assets](/workers/static-assets/).
Comment thread
hoodmane marked this conversation as resolved.

This is equivalent to FastAPI's native [`app.frontend()`](https://fastapi.tiangolo.com/tutorial/frontend/) method, which serves a static build directory as low-priority routes so that API path operations are checked first. The difference is where the files live: `app.frontend()` reads files from the local filesystem, while on Workers the static assets are served from Cloudflare's globally distributed asset store through the `ASSETS` binding. This means your frontend files are not bundled inside the Worker itself, keeping the bundle small.

Place your frontend build output (for example, HTML, CSS, and JavaScript files) in a directory such as `./public/`. Then configure your Wrangler file with an `assets` block that includes a `binding` and sets `run_worker_first` to `true`. This ensures every request reaches your FastAPI Worker first, so your API routes take priority over static files.
Place your frontend build output (for example, HTML, CSS, and JavaScript files)
in a directory such as `./public/`. Then configure your Wrangler file with an
`assets` block that includes a `binding` and sets `run_worker_first` to `true`.
This ensures every request reaches your FastAPI Worker first, so your API routes
take priority over static files.

Add a catch-all route at the end of your FastAPI app that proxies unmatched requests to the assets binding:

Expand Down Expand Up @@ -143,8 +164,7 @@ async def frontend(path: str, request: Request):
asset_url = f"https://assets.local/{path}"
resp = await env.ASSETS.fetch(asset_url)
body = await resp.bytes()
headers = dict(resp.headers)
return Response(content=body, status_code=resp.status, headers=headers)
return Response(content=body, status_code=resp.status, headers=resp.headers)
```

You can run this worker locally using `uv run pywrangler dev`.
Expand Down
173 changes: 173 additions & 0 deletions src/content/docs/workers/languages/python/packages/flask.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
pcx_content_type: reference
title: Flask
description: Run Flask applications in Python Workers.
head:
- tag: title
content: Flask
products:
- workers
---

import { Steps, WranglerConfig } from "~/components";

[Flask](https://flask.palletsprojects.com/) is supported in Python Workers.

Flask applications rely on a protocol called the Web Server Gateway Interface
(WSGI). This means that Flask never directly reads or writes to a socket,
instead relying on the WSGI server to communicate.

Python Workers include a [WSGI server](https://github.com/cloudflare/workers-py/blob/main/packages/runtime-sdk/src/workers/wsgi.py)
which you can use with Flask applications.

## Create a Flask Worker

Use this quick start to run a minimal Flask application.

<Steps>

1. Create `src/worker.py` with your flask application:

```python title="src/worker.py"
from flask import Flask
from workers import wsgi

app = Flask(__name__)

@app.get("/")
def index():
return {"message": "Hello from Flask"}

Default = wsgi.entrypoint(app)
Comment thread
hoodmane marked this conversation as resolved.
```

2. In the project root, create `wrangler.jsonc`:

<WranglerConfig>

```jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-flask-worker",
"main": "src/worker.py",
"compatibility_date": "$today",
"compatibility_flags": ["python_workers"]
}
```

</WranglerConfig>

3. Create a `pyproject.toml` to declare dependencies:

```toml title="pyproject.toml"
[project]
name = "flask-worker"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"flask",
]

[dependency-groups]
dev = [
"workers-py",
"workers-runtime-sdk",
]
```

4. Start the local development server:

```sh
uv run pywrangler dev
```

5. In another terminal, send a request to the Worker:

```sh
curl http://localhost:8787/
```

The Worker returns:

```json output
{"message":"Hello from Flask"}
```

</Steps>

## Serve a frontend

You can serve any static frontend alongside your flask backend by using [Workers Static Assets](/workers/static-assets/).
Using Static Assets means your frontend files are not bundled inside the Worker itself, keeping the bundle small.

Place your static files in a directory such as `./public/`. Then configure your
Wrangler file with an `assets` block that includes a `binding` and sets
`run_worker_first` to `true`. This ensures every request reaches your FastAPI
Worker first, so your API routes take priority over static files.

<WranglerConfig>

```jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-flask-worker",
"main": "src/worker.py",
"compatibility_date": "$today",
"compatibility_flags": ["python_workers"],
"assets": {
"directory": "./public/",
"binding": "ASSETS",
"run_worker_first": true
}
}
```

</WranglerConfig>

The following Worker handles an API route before forwarding other requests. The catch-all handlers return each asset's body, status, and headers:

```python title="src/worker.py"
from flask import Flask, Response, request
from pyodide.ffi import run_sync
from workers import wsgi


app = Flask(__name__)


@app.get("/api/hello")
def api_hello():
return {"message": "Hello from the API"}


@app.get("/")
@app.get("/<path:path>")
def frontend(path=""):
assets = request.environ["workers.env"].ASSETS
asset_response = run_sync(assets.fetch(f"https://assets.local/{path}"))
body = run_sync(asset_response.bytes())
return Response(
body,
status=asset_response.status,
headers=asset_response.headers,
Comment thread
hoodmane marked this conversation as resolved.
)


Default = wsgi.entrypoint(app)
```

`run_sync` bridges both asynchronous asset operations into Flask's synchronous
handler. API routes take priority, and unmatched paths are served from
`./public/`.
Comment on lines +159 to +161

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer not to expose pyodide FFIs in cloudflare docs if possible, maybe let's replace it with asyncio.run or asyncio.run_until_complete.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think those are confusing because they are not normally reentrant.



## More examples

Clone the `cloudflare/python-workers-examples` repository and run the flask-todo
example there:

```bash
git clone https://github.com/cloudflare/python-workers-examples
cd python-workers-examples/flask-todo
# See README.md for instructions
```
Loading