workers-runtime-apis について
Cloudflare Workers のランタイム API を使う開発者向けのリファレンススキルです。Fetch、Streams、Crypto、Cache、WebSockets、テキストエンコーディングを扱い、利用パターンと一般的なエラーへの対処法を紹介します。
主な機能
- 016分野のランタイムAPIを網羅
- 02Fetchのタイムアウトと再試行例
- 03ストリーム処理の解説
- 04暗号化とキャッシュの例
- 05一般的なエラーと予防策
活用シーン
- 01外部HTTPリクエストのタイムアウト設定
- 02大容量ファイルやストリームデータの処理
- 03レスポンスのキャッシュ
- 04リアルタイムWebSocket機能の構築
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill workers-runtime-apisインストール後、Claude Code で/workers-runtime-apisを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/cache-api.mdcrypto-api.mdencoding-api.mdfetch-api.mdstreams-api.mdwebsockets.mdtemplates/crypto-operations.tsfetch-patterns.tsstream-processing.tswebsocket-handler.ts詳細
Cloudflare Workers ランタイム API
Workers のランタイム API(Fetch、Streams、Crypto、Cache、WebSockets、テキストエンコーディング)を習得しましょう。
クイックリファレンス
| API | 用途 | 一般的な使用例 |
|---|---|---|
| Fetch | HTTP リクエスト | 外部 API、プロキシ |
| Streams | データストリーミング | 大容量ファイル、リアルタイム処理 |
| Crypto | 暗号化 | ハッシュ化、署名、暗号化 |
| Cache | レスポンスのキャッシュ | パフォーマンスの最適化 |
| WebSockets | リアルタイム接続 | チャット、ライブ更新 |
| Encoding | テキストエンコーディング | UTF-8、Base64 |
クイックスタート:Fetch API
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Basic fetch
const response = await fetch('https://api.example.com/data');
// With options
const postResponse = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${env.API_KEY}`,
},
body: JSON.stringify({ name: 'John' }),
});
// Clone for multiple reads
const clone = response.clone();
const json = await response.json();
const text = await clone.text();
return Response.json(json);
}
};
重要なルール
- 外部リクエストには必ずタイムアウトを設定する - Workers の制限時間は 30 秒ですが、外部 API は応答しなくなる可能性があります
- 本文を読み取る前にレスポンスを複製する - 本文は一度しか読み取れません
- 大きなペイロードにはストリーミングを使用する - レスポンス全体をメモリにバッファリングしないでください
- 外部 API のレスポンスをキャッシュする - レイテンシと API コストを削減できます
- Crypto 操作は try/catch で処理する - 入力が無効な場合、エラーがスローされます
- コスト削減には WebSocket の休止機能を使う - 休止機能を備えた Durable Objects を使用します
防止できる主なエラー 10 件
| エラー | 症状 | 防止方法 |
|---|---|---|
| 本文はすでに読み取り済み | TypeError: Body has already been consumed | 読み取る前にレスポンスを複製する |
| Fetch のタイムアウト | リクエストが停止し、Worker がタイムアウトする | AbortController とタイムアウトを使用する |
| 無効な JSON | SyntaxError: Unexpected token | パースする前に content-type を確認する |
| ストリームがロックされている | TypeError: ReadableStream is locked | ストリームを複数回読み取らない |
| Crypto キーエラー | DOMException: Invalid keyData | キーの形式とアルゴリズムを検証する |
| キャッシュミス | レスポンスではなく undefined を返す | 返す前にキャッシュを確認する |
| WebSocket の切断 | 接続が予期せず切断される | close イベントを処理し、再接続を実装する |
| エンコーディングエラー | TypeError: Invalid code point | TextEncoder/TextDecoder を正しく使用する |
| CORS によるブロック | ブラウザーがレスポンスを拒否する | 適切な CORS ヘッダーを追加する |
| リクエストサイズ | 413 Request Entity Too Large | 大きなアップロードをストリーミングする |
Fetch API のパターン
タイムアウトあり
async function fetchWithTimeout(url: string, timeout: number = 5000): Promise<Response> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
return response;
} finally {
clearTimeout(timeoutId);
}
}
リトライあり
async function fetchWithRetry(
url: string,
options: RequestInit = {},
retries: number = 3
): Promise<Response> {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
// Retry on 5xx errors
if (response.status >= 500 && i < retries - 1) {
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
continue;
}
return response;
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
}
}
throw new Error('Max retries exceeded');
}
Streams API
Transform Stream
function createUppercaseStream(): TransformStream<string, string> {
return new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
}
});
}
// Usage
const response = await fetch('https://example.com/text');
const transformed = response.body!
.pipeThrough(new TextDecoderStream())
.pipeThrough(createUppercaseStream())
.pipeThrough(new TextEncoderStream());
return new Response(transformed);
大きなレスポンスのストリーミング
async function streamLargeFile(url: string): Promise<Response> {
const response = await fetch(url);
// Stream directly without buffering
return new Response(response.body, {
headers: {
'Content-Type': response.headers.get('Content-Type') || 'application/octet-stream',
},
});
}
Crypto API
ハッシュ化
async function sha256(data: string): Promise<string> {
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(data);
const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
HMAC 署名
async function signHMAC(key: string, data: string): Promise<string> {
const encoder = new TextEncoder();
const keyData = encoder.encode(key);
const dataBuffer = encoder.encode(data);
const cryptoKey = await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, dataBuffer);
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}
Cache API
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default;
const cacheKey = new Request(request.url, { method: 'GET' });
// Check cache
let response = await cache.match(cacheKey);
if (response) {
return response;
}
// Fetch and cache
response = await fetch(request);
response = new Response(response.body, response);
response.headers.set('Cache-Control', 'public, max-age=3600');
// Store in cache (don't await)
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
}
};
WebSockets(Durable Objects)
// Durable Object with WebSocket hibernation
export class WebSocketRoom {
state: DurableObjectState;
constructor(state: DurableObjectState) {
this.state = state;
}
async fetch(request: Request): Promise<Response> {
const upgradeHeader = request.headers.get('Upgrade');
if (upgradeHeader !== 'websocket') {
return new Response('Expected websocket', { status: 426 });
}
const pair = new WebSocketPair();
const [client, server] = Object.values(pair);
// Accept with hibernation
this.state.acceptWebSocket(server);
return new Response(null, { status: 101, webSocket: client });
}
async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer) {
// Handle incoming message
const data = typeof message === 'string' ? message : new TextDecoder().decode(message);
// Broadcast to all connected clients
for (const client of this.state.getWebSockets()) {
client.send(data);
}
}
async webSocketClose(ws: WebSocket, code: number, reason: string) {
ws.close(code, reason);
}
}
リファレンスを読み込むタイミング
タスクに応じて、特定のリファレンスを読み込みます。
- HTTP リクエストを送信する場合 → タイムアウト、リトライ、プロキシのパターンについては
references/fetch-api.mdを読み込みます - 大きなデータを処理する場合 → TransformStream、チャンク分割については
references/streams-api.mdを読み込みます - 暗号化/署名を行う場合 → AES、RSA、JWT の検証については
references/crypto-api.mdを読み込みます - レスポンスをキャッシュする場合 → Cache API のパターン、TTL 戦略については
references/cache-api.mdを読み込みます - リアルタイム機能を実装する場合 → WebSocket のパターン、Durable Objects については
references/websockets.mdを読み込みます - テキストをエンコードする場合 → TextEncoder、Base64、Unicode については
references/encoding-api.mdを読み込みます
テンプレート
| テンプレート | 用途 | 使用する場面 |
|---|---|---|
templates/fetch-patterns.ts | HTTP リクエストユーティリティ | API クライアントの構築 |
templates/stream-processing.ts | ストリームの変換 | 大きなファイルの処理 |
templates/crypto-operations.ts | Crypto ユーティリティ | 署名、ハッシュ化、暗号化 |
templates/websocket-handler.ts | WebSocket DO | リアルタイムアプリケーション |
リソース
- ランタイム API:https://developers.cloudflare.com/workers/runtime-apis/
- Fetch API:https://developers.cloudflare.com/workers/runtime-apis/fetch/
- Streams API:https://developers.cloudflare.com/workers/runtime-apis/streams/
- Web Crypto:https://developers.cloudflare.com/workers/runtime-apis/web-crypto/
- Cache API:https://developers.cloudflare.com/workers/runtime-apis/cache/
- WebSockets:https://developers.cloudflare.com/workers/runtime-apis/websockets/
コメント
Markdown 対応