Cloudflare WorkersでPython Workerを試す、再び ⑤ Standard Library / Packages
前回
今回は標準ライブラリやパッケージについて。
標準ライブラリ
Python Workers では Pyodide を使用して Pythonコードを実行する。基本的に CPythonの大部分の機能・標準ライブラリは動作するが、一部例外がある。
- 標準ライブラリから削除されて使えないモジュール
- WebAssembly VM の制限で、機能しないモジュール
- 機能が制限されるモジュール
Pyodide を試したときにも確認したが、Pyodide 自身に存在する制約は Python Workers でも同じ、ということなのだろう。
Cloudflareのドキュメントに記載のモジュールは、多少異なるところもあるが概ね Pyodide のドキュメントに書いてあるものと同じみたい。
あと、Python Workers では、エフェメラルなインメモリファイルシステムが利用できる。open() や pathlib.path など 標準的な I/O 経由で利用はできるが、Worker Isolate が破棄されると当然ながらデータは失われる。一時ファイルの場合はこれでもOKだが、永続的なファイルシステムとして使う場合には KVやR2 など他のリソースサービスを使用する必要がある、
パッケージ
Python Workers では、Pywrangler を使ってパッケージとPython Workers を管理できる。Pythonパッケージはデプロイ時に Worker に自動的にバンドルされて利用可能となる。
利用したいパッケージは pyproject.toml に追加する。以下はFastAPIを追加した例。
[project]
name = "YourProjectName"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.13"
dependencies = [
"fastapi"
]
[dependency-groups]
dev = [
"workers-py",
"workers-runtime-sdk"
]
ただし、全てのパッケージがサポートされているわけではない。サポートされているライブラリは以下。
- PyPI上のPure Pythonパッケージ
-
PyEmscripten Pythonパッケージ
- (Pyodide 向けにビルドされたパッケージという認識)
- Pyodideに含まれるパッケージ
このあたりも 基本的に Pyodide と同じような感じに思える。
ドキュメントでは以下の主要なパッケージを追加する例が記載されている。
- Django
- FastAPI
- Flask
- LangChain
FastAPI を試してみる。
プロジェクト作成
uvx --from workers-py pywrangler init my-fastapi-sample \
--type hello-world \
--agents \
--git
プロジェクトディレクトリに移動
cd my-fastapi-sample
プロジェクトの初期状態はこう。
.
├── .git/
├── .gitignore
├── .python-version
├── .vscode/
│ └── settings.json
├── AGENTS.md
├── README.md
├── node_modules/
├── package-lock.json
├── package.json
├── pyproject.toml
├── src/
│ ├── entry.py
│ └── submodule.py
└── wrangler.jsonc
src の下をまるっと削除して、src/main.py を作成。
rm src/*.py
touch src/main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
from workers import asgi
Default = asgi.entrypoint(app)
wrangler.jsonc のエントリーポイントを修正して、src/main.py に向ける。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-fastapi-sample",
// 以下を修正
"main": "src/main.py",
"compatibility_date": "2026-10-06",
"compatibility_flags": [
"python_workers"
],
"observability": {
"enabled": true
}
}
pyproject.toml に fastapi パッケージを追加
[project]
name = "my-fastapi-sample"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"fastapi",
]
[dependency-groups]
dev = ["workers-py", "workers-runtime-sdk"]
開発サーバを起動
uv run pywrangler dev
curl で確認
curl -s http://localhost:8787 \
-H "Content-Type: application/json" \
| jq -r .
{
"Hello": "World"
}
/docs で API ドキュメント も確認できる。

ここに Workers Static Assets で、静的コンテンツを配信するフロントエンドを追加する。
静的コンテンツを用意。中身はChatGPTに作成してもらった。
mkdir public
touch public/{index.html,style.css,app.js}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>FastAPI on Cloudflare Workers</title>
<link rel="stylesheet" href="/style.css">
<script src="/app.js" defer></script>
</head>
<body>
<h1>FastAPI on Cloudflare Workers</h1>
<p>これは静的HTMLです。</p>
<button id="button">APIを呼び出す</button>
<p id="result"></p>
</body>
</html>
body {
font-family: sans-serif;
max-width: 600px;
margin: 50px auto;
background: #f0f4f8;
}
h1 {
color: #2563eb;
}
button {
padding: 10px 20px;
cursor: pointer;
}
document.getElementById("button").onclick = async () => {
const response = await fetch("/api/hello");
const data = await response.json();
document.getElementById("result").textContent = data.message;
};
wrangler.jsonc を修正。ASSETS バインディングを使うことで、静的コンテンツは Worker にバンドルされることなく、Cloudflare の分散アセットストアから配信されるらしい。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-fastapi-sample",
"main": "src/main.py",
"compatibility_date": "2026-10-06",
"compatibility_flags": [
"python_workers"
],
"observability": {
"enabled": true
},
// 以下を追加
"assets": {
"directory": "./public/",
"binding": "ASSETS",
// すべてのリクエストが最初にFastAPI Workerに送信され、
// APIルートが静的ファイルよりも優先される
"run_worker_first": true
}
}
src/main.py を修正。
from fastapi import FastAPI, Request
from fastapi.responses import Response
from workers import asgi
app = FastAPI()
Default = asgi.entrypoint(app)
@app.get("/api/hello")
async def api_hello():
return {"message": "Hello from the API"}
# Catch-all: 上記以外のリクエストをすべて Workers Static Assets にプロキシする
# FastASPIのネイティブな app.frontend("/", directory="dist") に相当する Workers の実装
@app.get("/{path:path}")
async def frontend(path: str, request: Request):
env = request.scope["env"]
asset_url = f"https://assets.local/{path}"
resp = await env.ASSETS.fetch(asset_url)
body = await resp.bytes()
return Response(content=body, status_code=resp.status, headers=resp.headers)
開発サーバで試してみる。
uv run pywrangler dev
ブラウザで開いて「APIを呼び出す」をクリック。

APIの結果が反映されている。

curl でも。
curl -s http://localhost:8787/
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>FastAPI on Cloudflare Workers</title>
<link rel="stylesheet" href="/style.css">
<script src="/app.js" defer></script>
</head>
<body>
<h1>FastAPI on Cloudflare Workers</h1>
<p>これは静的HTMLです。</p>
<button id="button">APIを呼び出す</button>
<p id="result"></p>
</body>
</html>
curl -s http://localhost:8787/api/hello \
-H "Content-Type: application/json" \
| jq -r .
{
"message": "Hello from the API"
}
まとめ
ライブラリ・パッケージについては、やはり Pyodide の制約を受けるという点に注意が必要。でも APIやSPAとか作るには良さそうな気がした。
実際に作るにあたっては以下にサンプルが多数用意されているので、参考にするのが良さそう。
サンプルとかも写経的に試してみる