よくある構成は、Static Assets で静的なフロントエンドアプリ(React、Vue、Svelte など)を配信し、バックエンドへのリクエストをコンテナ化したバックエンドアプリへ渡す方法です。
この例では、静的アセットとして配信するシンプルな index.html を使います。フロントエンドフレームワークはほかにも選べます。詳細は Workers のフレームワーク例 を参照してください。
完全な例は Static Frontend + Container Backend Template ↗ を参照してください。
{
"name": "cron-container",
"main": "src/index.ts",
"assets": {
"directory": "./dist",
"binding": "ASSETS"
},
"containers": [
{
"class_name": "Backend",
"image": "./Dockerfile",
"max_instances": 3
}
],
"durable_objects": {
"bindings": [
{
"class_name": "Backend",
"name": "BACKEND"
}
]
},
"migrations": [
{
"new_sqlite_classes": [
"Backend"
],
"tag": "v1"
}
]
}name = "cron-container"
main = "src/index.ts"
[assets]
directory = "./dist"
binding = "ASSETS"
[[containers]]
class_name = "Backend"
image = "./Dockerfile"
max_instances = 3
[[durable_objects.bindings]]
class_name = "Backend"
name = "BACKEND"
[[migrations]]
new_sqlite_classes = [ "Backend" ]
tag = "v1"./dist ディレクトリに、シンプルな index.html ファイルを作成します。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Widgets</title>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.13.3/cdn.min.js"></script>
</head>
<body>
<div x-data="widgets()" x-init="fetchWidgets()">
<h1>Widgets</h1>
<div x-show="loading">Loading...</div>
<div x-show="error" x-text="error" style="color: red;"></div>
<ul x-show="!loading && !error">
<template x-for="widget in widgets" :key="widget.id">
<li>
<span x-text="widget.name"></span> - (ID: <span x-text="widget.id"></span>)
</li>
</template>
</ul>
<div x-show="!loading && !error && widgets.length === 0">
No widgets found.
</div>
</div>
<script>
function widgets() {
return {
widgets: [],
loading: false,
error: null,
async fetchWidgets() {
this.loading = true;
this.error = null;
try {
const response = await fetch('/api/widgets');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
this.widgets = await response.json();
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
}
}
}
</script>
</body>
</html>この例では、Alpine.js ↗ を使って /api/widgets からウィジェット一覧を取得します。
ごく簡単な例ですが、構成はかなり複雑にもできます。詳細は フロントエンドフレームワークと Workers を組み合わせる例 を参照してください。
Worker は、静的アセットの配信と、コンテナ化したバックエンドへのリクエスト振り分けの両方を行う必要があります。
この例では、ルートが /api で始まる場合は 3 つのコンテナインスタンスのいずれかにリクエストを渡し、それ以外は静的アセットとして配信します。
import { Container, getRandom } from "@cloudflare/containers";
const INSTANCE_COUNT = 3;
class Backend extends Container {
defaultPort = 8080; // pass requests to port 8080 in the container
sleepAfter = "2h"; // only sleep a container if it hasn't gotten requests in 2 hours
}
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname.startsWith("/api")) {
const containerInstance = await getRandom(env.BACKEND, INSTANCE_COUNT);
return containerInstance.fetch(request);
}
return env.ASSETS.fetch(request);
},
};コンテナは /api/widgets へのリクエストを処理できる必要があります。
この例では、ハードコードしたウィジェット一覧を返すシンプルな Golang バックエンドを使います。
server.go
package main
import (
"encoding/json"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r \*http.Request) {
widgets := []map[string]interface{}{
{"id": 1, "name": "Widget A"},
{"id": 2, "name": "Sprocket B"},
{"id": 3, "name": "Gear C"},
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
json.NewEncoder(w).Encode(widgets)
}
func main() {
http.HandleFunc("/api/widgets", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}