cloudflare-workers-runtime-apis — Claude Code skill: Fetch, Streams
A skill covering the Cloudflare Workers runtime APIs: Fetch, Streams, Crypto, Cache, WebSockets and text encoding. Handy when a worker trips over requests, streams or cache.
npx skills add secondsky/claude-skills --skill cloudflare-workers-runtime-apis -a claude-codeWhat cloudflare-workers-runtime-apis does
The skill walks through six API groups of the Workers runtime and the mistakes each one invites. Inside is a quick reference table, a starter worker using fetch, a table of common errors with symptoms and prevention, plus ready patterns: a request with a timeout via AbortController, retry with exponential backoff, streaming a response, SHA-256 hashing, HMAC signing, caching through caches.default, and a WebSocket room on Durable Objects with hibernation.
The rules it pushes are plain. Clone a response before reading the body, since a body reads once. Set timeouts on external requests: a worker has a 30s limit and a third-party API can just hang. Stream large payloads instead of buffering them. Wrap crypto.subtle calls in try/catch, because a bad key format or algorithm throws.
This is a text skill: a SKILL.md with TypeScript examples, no scripts and nothing to install. The examples pull secrets like API_KEY from env, so you set those in the worker itself. The WebSockets section stops at the Upgrade header check, so hibernation gets a general pointer rather than a full walkthrough.
What it helps with
- make an external request from a worker with a timeout so it cannot hang
- read a response body twice without the consumed Body error
- stream a large file instead of buffering it in memory
- compute SHA-256 or sign data with HMAC
- cache an external API response to cut latency
Example prompts for the agent
- show me how to clone a response before reading the body in Cloudflare Workers
- I need a fetch with AbortController and a 5 second timeout in a worker
- how do I cache an external API response with caches.default
How to install cloudflare-workers-runtime-apis
1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill cloudflare-workers-runtime-apis -a claude-code2Manually: put the files in place
~/.claude/skills/cloudflare-workers-runtime-apis/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/cloudflare-workers-runtime-apis/
1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill cloudflare-workers-runtime-apis -a codex2Manually: put the files in place
.agents/skills/cloudflare-workers-runtime-apis/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill cloudflare-workers-runtime-apis -a cursor2Manually: put the files in place
.cursor/skills/cloudflare-workers-runtime-apis/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install secondsky/claude-skills cloudflare-workers-runtime-apis2Manually: put the files in place
.github/skills/cloudflare-workers-runtime-apis/SKILL.md1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill cloudflare-workers-runtime-apis -a gemini-cli2Platform CLI
gemini skills install https://github.com/secondsky/claude-skills --path plugins/cloudflare-workers/skills/cloudflare-workers-runtime-apisManually: put the files in place
.devin/skills/cloudflare-workers-runtime-apis/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill cloudflare-workers-runtime-apis -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about cloudflare-workers-runtime-apis
How do I install cloudflare-workers-runtime-apis in Claude?
Run npx skills add secondsky/claude-skills --skill cloudflare-workers-runtime-apis -a claude-code. The skill then lives at ~/.claude/skills/cloudflare-workers-runtime-apis/SKILL.md.
What does cloudflare-workers-runtime-apis do?
It gathers Workers runtime API patterns in one file: Fetch, Streams, Crypto, Cache, WebSockets and text encoding. It also lists common errors and how to avoid them.
Does this skill need any dependencies?
No, it is a SKILL.md with TypeScript examples. Secrets such as API_KEY come from env in the examples, so you configure them in the worker.
Source text
SKILL.md · MIT
---
name: cloudflare-workers-runtime-apis
description: Cloudflare Workers Runtime APIs including Fetch, Streams, Crypto, Cache, WebSockets, and Encoding. Use for HTTP requests, streaming, encryption, caching, real-time connections, or encountering API compatibility, response handling, stream processing errors.
license: MIT
---
# Cloudflare Workers Runtime APIs
Master the Workers runtime APIs: Fetch, Streams, Crypto, Cache, WebSockets, and text encoding.
## Quick Reference
| API | Purpose | Common Use |
|-----|---------|------------|
| **Fetch** | HTTP requests | External APIs, proxying |
| **Streams** | Data streaming | Large files, real-time |
| **Crypto** | Cryptography | Hashing, signing, encryption |
| **Cache** | Response caching | Performance optimization |
| **WebSockets** | Real-time connections | Chat, live updates |
| **Encoding** | Text encoding | UTF-8, Base64 |
## Quick Start: Fetch API
```typescript
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);
}
};
```
## Critical Rules
1. **Always set timeouts for external requests** - Workers have a 30s limit, external APIs can hang
2. **Clone responses before reading body** - Body can only be read once
3. **Use streaming for large payloads** - Don't buffer entire response in memory
4. **Cache external API responses** - Reduce latency and API costs
5. **Handle Crypto operations in try/catch** - Invalid inputs throw errors
6. **WebSocket hibernation for cost** - Use Durable Objects with hibernation
## Top 10 Errors Prevented
| Error | Symptom | Prevention |
|-------|---------|------------|
| Body already read | `TypeError: Body has already been consumed` | Clone response before reading |
| Fetch timeout | Request hangs, worker times out | Use `AbortController` with timeout |
| Invalid JSON | `SyntaxError: Unexpected token` | Check content-type before parsing |
| Stream locked | `TypeError: ReadableStream is locked` | Don't read stream multiple times |
| Crypto key error | `DOMException: Invalid keyData` | Validate key format and algorithm |
| Cache miss | Returns `undefined` instead of response | Check cache before returning |
| WebSocket close | Connection drops unexpectedly | Handle close event, implement reconnect |
| Encoding error | `TypeError: Invalid code point` | Use TextEncoder/TextDecoder properly |
| CORS blocked | Browser rejects response | Add proper CORS headers |
| Request size | `413 Request Entity Too Large` | Stream large uploads |
## Fetch API Patterns
### With Timeout
```typescript
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);
}
}
```
### With Retry
```typescript
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
```typescript
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);
```
### Stream Large Response
```typescript
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
### Hashing
```typescript
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 Signing
```typescript
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
```typescript
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)
```typescript
// 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);
}
}
```
## When to Load References
Load specific references based on the task:
- **Making HTTP requests?** → Load `references/fetch-api.md` for timeout, retry, proxy patterns
- **Processing large data?** → Load `references/streams-api.md` for TransformStream, chunking
- **Encryption/signing?** → Load `references/crypto-api.md` for AES, RSA, JWT verification
- **Caching responses?** → Load `references/cache-api.md` for Cache API patterns, TTL strategies
- **Real-time features?** → Load `references/websockets.md` for WebSocket patterns, Durable Objects
- **Text encoding?** → Load `references/encoding-api.md` for TextEncoder, Base64, Unicode
## Templates
| Template | Purpose | Use When |
|----------|---------|----------|
| `templates/fetch-patterns.ts` | HTTP request utilities | Building API clients |
| `templates/stream-processing.ts` | Stream transformation | Processing large files |
| `templates/crypto-operations.ts` | Crypto utilities | Signing, hashing, encryption |
| `templates/websocket-handler.ts` | WebSocket DO | Real-time applications |
## Resources
- Runtime APIs: 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/
© secondsky · MIT · secondsky/claude-skills