-
Notifications
You must be signed in to change notification settings - Fork 16.5k
Add flask in Python workers documentation #33074
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| --- | ||
| 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) | ||
|
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, | ||
|
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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| ``` | ||
Uh oh!
There was an error while loading. Please reload this page.