Cloudflare Workersではじめる
このガイドでは、Cloudflare Workers上でZeltアプリケーションをゼロから構築する手順を説明します。
前提条件
Cloudflare Workersでは追加で以下が必要です:
- Wrangler CLI
- Cloudflareアカウント(無料枠あり)
インストール
pnpm add @zeltjs/core @zeltjs/adapter-cloudflare-workers
pnpm add -D wrangler @cloudflare/workers-types
プロジェクト構成
my-app/
├── src/
│ ├── entry/
│ │ ├── controllers/ # HTTPエンドポイント
│ │ └── commands/ # CLIコマンド
│ ├── services/ # ビジネスロジック
│ ├── configs/ # 設定クラス
│ ├── app.ts # アプリケーション定義
│ ├── cli.ts # CLIエントリポイント
│ └── main.ts # HTTPサーバーエントリポイント
├── package.json
└── tsconfig.json
| ディレクトリ | 用途 |
|---|---|
entry/ | 外部向けエントリポイント(HTTP、CLI) |
services/ | ビジネスロジック、DIで注入される |
configs/ | 環境変数と設定 |
Cloudflare Workersの場合、プロジェクトルートに wrangler.toml も追加します。
Hello World
Step 1: Controllerを作成する
Controllerは受信したHTTPリクエストを処理し、レスポンスを返します。各controllerは @Controller でデコレートされたクラスで、ルートのprefixを定義します。
src/entry/controllers/hello.controller.ts を作成します:
@Controller('/hello')
export class HelloController {
@Get('/:name')
greet(req = request()) {
const name = req.pathParam('name');
return { message: `Hello, ${name}!` };
}
}
@Controller('/hello')— このcontroller内の全ルートの基本パスを設定します@Get('/:name')—/hello/:nameへのGETリクエストを処理しますreq.pathParam('name')— URLパスからnameパラメータを取り出します
Step 2: アプリケーションを作成する
src/app.ts を作成し、controllerを結線します:
export const app = createApp([http({
controllers: [HelloController],
})]);
Step 3: Workerのentryを作成する
Cloudflare Workersのentryとして src/index.ts を作成します:
const workers = await onCloudflareWorkers(app);
export default { fetch: workers.fetch };
onCloudflareWorkers() 関数は非同期で、アプリをWorkersランタイム用に準備します。返されるオブジェクトには fetch ハンドラに加えて、shutdown やserviceにアクセスするための get などのユーティリティが含まれます。デフォルトでは遅延初期化(warmup: false)が使われます — controllerは起動時ではなく最初のリクエストで解決されます。これによりサーバーレス環境でのコールドスタート時間が最適化されます。
Step 4: Wranglerを設定する
wrangler.toml を作成します:
name = "my-zelt-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
[vars]
API_HOST = "https://api.example.com"
Step 5: TypeScriptを設定する
tsconfig.json を作成します:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"types": ["@cloudflare/workers-types"]
},
"include": ["src"]
}
Step 6: ローカルで実行する
npx wrangler dev
http://localhost:8787/hello/world にアクセスすると、次のように表示されます:
{ "message": "Hello, world!" }
設定
環境変数
Cloudflare Workersでは、環境変数は wrangler.toml で設定し、Env 経由でアクセスします。
@Controller('/config')
export class ConfigController {
constructor(private env = inject(Env)) {}
@Get('/api-host')
getApiHost() {
return { apiHost: this.env.getString('API_HOST', 'localhost') };
}
}
export const app = createApp([http({
controllers: [ConfigController],
})]);
重要: onCloudflareWorkers() はenv adaptorを自動で登録するため、inject(Env) は追加設定なしにWorkersランタイム(cloudflare:workers モジュール)から環境変数を読み取ります。
Secrets
機密情報には [vars] の代わりにWrangler secretsを使います:
npx wrangler secret put DATABASE_URL
アクセス方法は同じく Env 経由です:
get connectionUrl() {
return this.env.getString('DATABASE_URL', '');
}
}
Cloudflare Bindings
Wranglerのbinding(D1、KV、R2、Durable Objectsなど)は Env で公開される文字列のみの環境変数とは別に、fetch(request, env, ctx) の env として渡されます。wrangler types が生成する型でこれらにアクセスするには CloudflareBindings を使います:
@Controller('/data')
export class DataController {
constructor(private bindings = inject(CloudflareBindings)) {}
@Get('/:key')
async get(req = request()) {
const value = await this.bindings.get('CACHE').get(req.pathParam('key'));
return { value };
}
}
bindings.get('CACHE') は拡張されたグローバルな Env から型をそのまま読み取るため、手動での型付けなしに KVNamespace(あるいは宣言したbindingに応じて D1Database、R2Bucket など)として型付けされます。
重要: CloudflareBindings.get() は onCloudflareWorkers() によって設定されるリクエストスコープのストレージから読み取るため、リクエスト処理中でのみ動作します — リクエスト外(例えばモジュール読み込み時)で呼び出すとエラーになります。
Services
ServiceはNode.jsとまったく同じ仕組みで動作します。クラスをserviceとしてマークするには @Injectable を使います。
@Injectable()
export class GreetingService {
greet(name: string): string {
return `Hello, ${name}!`;
}
}
controllerに注入します:
@Controller('/hello')
export class HelloController {
constructor(private greetingService = inject(GreetingService)) {}
@Get('/:name')
greet(req = request()) {
const name = req.pathParam('name');
return { message: this.greetingService.greet(name) };
}
}
デプロイ
WorkerをCloudflareのグローバルネットワークにデプロイします:
npx wrangler deploy
Workerは https://my-zelt-worker.<your-subdomain>.workers.dev で利用できるようになります。
応用: Warmupオプション
デフォルトでは、onCloudflareWorkers() はコールドスタート時間を最小化するために遅延初期化(warmup: false)を使用します。controllerは最初のリクエストで解決されます。
初期化時に全controllerを解決したい場合(デバッグ時や、コールドスタート時間があまり重要でない場合に有用)は warmup: true を設定します:
const workers = await onCloudflareWorkers(app, { warmup: true });
export default { fetch: workers.fetch };
| オプション | 挙動 | 用途 |
|---|---|---|
warmup: false(デフォルト) | controllerは最初のリクエストで解決される | コールドスタートの最適化 |
warmup: true | 全controllerが初期化時に解決される | デバッグ、ウォームな環境 |
次のステップ
基本的なworkerが動くようになったので、他の機能も見てみましょう:
- Controllers — ルーティングとHTTPメソッド
- Services — ビジネスロジックと依存性注入
- Validation — Valibotによるリクエストボディのバリデーション
- Middleware — リクエスト/レスポンスのインターセプタ
- Configuration — 高度な設定パターン