diff --git a/src/content/docs/workers/framework-guides/apis/flask.mdx b/src/content/docs/workers/framework-guides/apis/flask.mdx
new file mode 100644
index 00000000000..92b4d5d7f4b
--- /dev/null
+++ b/src/content/docs/workers/framework-guides/apis/flask.mdx
@@ -0,0 +1,8 @@
+---
+pcx_content_type: navigation
+title: Flask
+description: Deploy Flask applications on Cloudflare Workers with Python support.
+external_link: /workers/languages/python/packages/flask/
+products:
+ - workers
+---
diff --git a/src/content/docs/workers/languages/python/packages/fastapi.mdx b/src/content/docs/workers/languages/python/packages/fastapi.mdx
index a8b09a35662..fd29c73c615 100644
--- a/src/content/docs/workers/languages/python/packages/fastapi.mdx
+++ b/src/content/docs/workers/languages/python/packages/fastapi.mdx
@@ -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:
+
+
+1. Create a `src/main.py` file with your FastAPI application:
```python
from fastapi import FastAPI
@@ -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:
```jsonc
{
+ "$schema": "node_modules/wrangler/config-schema.json",
"name": "my-fastapi-app",
"main": "src/main.py",
"compatibility_date": "$today",
@@ -53,7 +56,7 @@ Default = asgi.entrypoint(app)
```
-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"
@@ -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"}
+ ```
+
+
+
## 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/).
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:
@@ -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`.
diff --git a/src/content/docs/workers/languages/python/packages/flask.mdx b/src/content/docs/workers/languages/python/packages/flask.mdx
new file mode 100644
index 00000000000..0e39043f653
--- /dev/null
+++ b/src/content/docs/workers/languages/python/packages/flask.mdx
@@ -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.
+
+
+
+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)
+ ```
+
+2. In the project root, create `wrangler.jsonc`:
+
+
+
+ ```jsonc
+ {
+ "$schema": "node_modules/wrangler/config-schema.json",
+ "name": "my-flask-worker",
+ "main": "src/worker.py",
+ "compatibility_date": "$today",
+ "compatibility_flags": ["python_workers"]
+ }
+ ```
+
+
+
+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"}
+ ```
+
+
+
+## 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.
+
+
+
+```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
+ }
+}
+```
+
+
+
+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("/")
+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,
+ )
+
+
+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/`.
+
+
+## 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
+```