Files
gh-tenequm-claude-plugins-c…/skills/skill/references/advanced-features.md
2025-11-30 09:01:09 +08:00

14 KiB

Advanced Features

Advanced Workers capabilities for complex applications and enterprise use cases.

Workers for Platforms

Deploy and manage customer-provided Workers on your infrastructure.

Use cases:

  • SaaS platforms where customers write custom code
  • Low-code/no-code platforms
  • Plugin systems
  • Custom business logic hosting

Setup

wrangler.toml:

[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "my-platform"

# Optional outbound Worker
outbound = { service = "my-outbound-worker" }

Dynamic Dispatch

Route requests to customer Workers dynamically.

Dispatch Worker:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const customerId = url.hostname.split(".")[0];

    // Get customer's Worker
    const userWorker = env.DISPATCHER.get(customerId);

    // Forward request to customer's Worker
    return userWorker.fetch(request);
  },
};

Upload User Workers

Via API:

async function uploadUserWorker(
  customerId: string,
  code: string,
  env: Env
): Promise<void> {
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/accounts/${env.ACCOUNT_ID}/workers/dispatch/namespaces/${env.NAMESPACE}/scripts/${customerId}`,
    {
      method: "PUT",
      headers: {
        "Authorization": `Bearer ${env.API_TOKEN}`,
        "Content-Type": "application/javascript",
      },
      body: code,
    }
  );

  if (!response.ok) {
    throw new Error(`Failed to upload: ${await response.text()}`);
  }
}

Via Wrangler:

wrangler dispatch-namespace put my-platform customer-123 ./customer-worker.js

Outbound Workers

Control what customer Workers can access.

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Validate request from customer Worker
    const url = new URL(request.url);

    // Block certain domains
    const blockedDomains = ["internal.example.com"];
    if (blockedDomains.some((d) => url.hostname.includes(d))) {
      return new Response("Forbidden", { status: 403 });
    }

    // Add authentication
    request.headers.set("X-Platform-Auth", env.PLATFORM_SECRET);

    // Forward to destination
    return fetch(request);
  },
};

Limits and Quotas

Set limits for customer Workers.

[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "my-platform"

[dispatch_namespaces.outbound]
service = "outbound-worker"
parameters = {
  cpu_ms = 50,
  requests = 1000
}

Smart Placement

Automatically place Workers near data sources to reduce latency.

Enable Smart Placement

wrangler.toml:

[placement]
mode = "smart"

How it works:

  • Workers monitors where your Worker makes subrequests
  • Automatically places future executions near those data sources
  • Reduces round-trip time for database/API calls
  • No code changes required

Best for:

  • Database-heavy Workers
  • Workers making many external API calls
  • Geographically distributed data sources

Limitations:

  • Not compatible with Durable Objects
  • Requires Workers Standard plan

WebSockets

Build real-time applications with WebSockets.

Basic WebSocket Server

export default {
  async fetch(request: Request): Promise<Response> {
    const upgradeHeader = request.headers.get("Upgrade");

    if (upgradeHeader !== "websocket") {
      return new Response("Expected WebSocket", { status: 426 });
    }

    const [client, server] = Object.values(new WebSocketPair());

    // Handle WebSocket messages
    server.accept();

    server.addEventListener("message", (event) => {
      console.log("Received:", event.data);

      // Echo message back
      server.send(`Echo: ${event.data}`);
    });

    server.addEventListener("close", (event) => {
      console.log("WebSocket closed:", event.code, event.reason);
    });

    server.addEventListener("error", (event) => {
      console.error("WebSocket error:", event);
    });

    return new Response(null, {
      status: 101,
      webSocket: client,
    });
  },
};

WebSocket with Durable Objects

Use Durable Objects for coordinated WebSocket servers.

Durable Object:

export class ChatRoom {
  state: DurableObjectState;
  sessions: Set<WebSocket>;

  constructor(state: DurableObjectState) {
    this.state = state;
    this.sessions = new Set();
  }

  async fetch(request: Request): Promise<Response> {
    const [client, server] = Object.values(new WebSocketPair());

    server.accept();
    this.sessions.add(server);

    server.addEventListener("message", (event) => {
      // Broadcast to all connected clients
      this.broadcast(event.data as string);
    });

    server.addEventListener("close", () => {
      this.sessions.delete(server);
    });

    return new Response(null, {
      status: 101,
      webSocket: client,
    });
  }

  broadcast(message: string) {
    for (const session of this.sessions) {
      try {
        session.send(message);
      } catch (error) {
        // Remove failed sessions
        this.sessions.delete(session);
      }
    }
  }
}

Worker:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const roomId = url.pathname.substring(1) || "default";

    // Get Durable Object for this room
    const id = env.CHAT_ROOM.idFromName(roomId);
    const room = env.CHAT_ROOM.get(id);

    return room.fetch(request);
  },
};

WebSocket Hibernation

Reduce costs by hibernating idle WebSocket connections.

export class HibernatingChatRoom {
  state: DurableObjectState;

  constructor(state: DurableObjectState) {
    this.state = state;

    // Enable hibernation
    state.setWebSocketAutoResponse(
      new WebSocketRequestResponsePair("ping", "pong")
    );
  }

  async fetch(request: Request): Promise<Response> {
    const [client, server] = Object.values(new WebSocketPair());

    // Accept with hibernation
    this.state.acceptWebSocket(server);

    return new Response(null, {
      status: 101,
      webSocket: client,
    });
  }

  async webSocketMessage(ws: WebSocket, message: string) {
    // Called when message received (Worker woken up)
    const data = JSON.parse(message);

    // Broadcast to all
    this.state.getWebSockets().forEach((socket) => {
      socket.send(message);
    });
  }

  async webSocketClose(ws: WebSocket, code: number, reason: string) {
    // Cleanup on close
    ws.close(code, reason);
  }
}

Streaming

Stream responses for large datasets.

Streaming JSON

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { readable, writable } = new TransformStream();
    const writer = writable.getWriter();
    const encoder = new TextEncoder();

    // Stream in background
    (async () => {
      try {
        await writer.write(encoder.encode("["));

        const results = await env.DB.prepare(
          "SELECT * FROM large_table"
        ).all();

        for (let i = 0; i < results.results.length; i++) {
          const item = results.results[i];
          await writer.write(encoder.encode(JSON.stringify(item)));

          if (i < results.results.length - 1) {
            await writer.write(encoder.encode(","));
          }
        }

        await writer.write(encoder.encode("]"));
        await writer.close();
      } catch (error) {
        await writer.abort(error);
      }
    })();

    return new Response(readable, {
      headers: { "Content-Type": "application/json" },
    });
  },
};

Server-Sent Events (SSE)

export default {
  async fetch(request: Request): Promise<Response> {
    const { readable, writable } = new TransformStream();
    const writer = writable.getWriter();
    const encoder = new TextEncoder();

    // Send events in background
    (async () => {
      try {
        for (let i = 0; i < 10; i++) {
          await writer.write(
            encoder.encode(`data: ${JSON.stringify({ count: i })}\n\n`)
          );
          await new Promise((resolve) => setTimeout(resolve, 1000));
        }

        await writer.close();
      } catch (error) {
        await writer.abort(error);
      }
    })();

    return new Response(readable, {
      headers: {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
      },
    });
  },
};

Custom Domains

Configure custom domains for Workers.

Via Wrangler

routes = [
  { pattern = "api.example.com/*", zone_name = "example.com" }
]

Via Dashboard

  1. Navigate to Workers & Pages
  2. Select your Worker
  3. Go to Settings > Triggers
  4. Add Custom Domain

Multiple Domains

routes = [
  { pattern = "api.example.com/*", zone_name = "example.com" },
  { pattern = "api.other-domain.com/*", zone_name = "other-domain.com" }
]

Static Assets

Serve static files with Workers.

Configuration

wrangler.toml:

[assets]
directory = "./public"
binding = "ASSETS"

# HTML handling
html_handling = "auto-trailing-slash"

# 404 handling
not_found_handling = "single-page-application"

Custom Asset Handling

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // API routes
    if (url.pathname.startsWith("/api/")) {
      return handleAPI(request, env);
    }

    // Static assets
    try {
      const asset = await env.ASSETS.fetch(request);

      // Add custom headers
      const response = new Response(asset.body, asset);
      response.headers.set("X-Custom-Header", "value");

      return response;
    } catch {
      return new Response("Not found", { status: 404 });
    }
  },
};

Framework Integration

Next.js:

npm create cloudflare@latest my-next-app -- --framework=next

Remix:

npm create cloudflare@latest my-remix-app -- --framework=remix

Astro:

npm create cloudflare@latest my-astro-app -- --framework=astro

TCP Sockets

Connect to external services via TCP.

export default {
  async fetch(request: Request): Promise<Response> {
    const socket = connect({
      hostname: "example.com",
      port: 6379, // Redis
    });

    const writer = socket.writable.getWriter();
    const encoder = new TextEncoder();

    // Send Redis command
    await writer.write(encoder.encode("PING\r\n"));

    // Read response
    const reader = socket.readable.getReader();
    const { value } = await reader.read();
    const response = new TextDecoder().decode(value);

    return Response.json({ response });
  },
};

HTML Rewriter

Transform HTML on the fly.

class LinkRewriter {
  element(element: Element) {
    const href = element.getAttribute("href");

    if (href && href.startsWith("/")) {
      // Make absolute
      element.setAttribute("href", `https://example.com${href}`);
    }

    // Add tracking
    element.setAttribute("data-tracked", "true");
  }
}

export default {
  async fetch(request: Request): Promise<Response> {
    const response = await fetch(request);

    return new HTMLRewriter()
      .on("a", new LinkRewriter())
      .on("img", {
        element(element) {
          // Lazy load images
          element.setAttribute("loading", "lazy");
        },
      })
      .transform(response);
  },
};

Scheduled Events (Cron)

Run Workers on a schedule.

wrangler.toml:

[triggers]
crons = [
  "0 0 * * *",     # Daily at midnight
  "*/15 * * * *",  # Every 15 minutes
  "0 9 * * 1-5"    # Weekdays at 9 AM
]

Handler:

export default {
  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
    console.log("Cron trigger:", event.cron);

    // Cleanup old data
    await env.DB.prepare("DELETE FROM logs WHERE created_at < ?")
      .bind(Date.now() - 30 * 24 * 60 * 60 * 1000)
      .run();

    // Send daily report
    ctx.waitUntil(sendDailyReport(env));
  },
};

Email Handler

Process incoming emails.

wrangler.toml:

[email]
name = "support@example.com"

Handler:

export default {
  async email(message: ForwardableEmailMessage, env: Env) {
    console.log("From:", message.from);
    console.log("Subject:", message.headers.get("subject"));

    // Forward to support team
    await message.forward("support-team@example.com");

    // Or process email
    const rawEmail = await new Response(message.raw).text();
    await env.EMAILS.put(message.headers.get("message-id"), rawEmail);
  },
};

Tail Workers

Monitor and log other Workers.

wrangler.toml:

[tail_consumers]
service = "my-logging-worker"

Tail Worker:

export default {
  async tail(events: TraceItem[], env: Env) {
    for (const event of events) {
      if (event.outcome === "exception") {
        // Log errors to external service
        await fetch("https://logs.example.com", {
          method: "POST",
          body: JSON.stringify({
            scriptName: event.scriptName,
            error: event.exceptions,
            timestamp: event.event.request.timestamp,
          }),
        });
      }
    }
  },
};

Multi-Region Deployments

Deploy Workers to specific regions (Enterprise only).

[placement]
mode = "regional"
regions = ["us-east", "eu-west"]

Workers Analytics Engine

Write custom metrics.

[[analytics_engine_datasets]]
binding = "ANALYTICS"
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const start = Date.now();
    const response = await handleRequest(request, env);
    const duration = Date.now() - start;

    // Write custom metrics
    env.ANALYTICS.writeDataPoint({
      blobs: [request.url, request.method, String(response.status)],
      doubles: [duration],
      indexes: [request.headers.get("user-agent") || "unknown"],
    });

    return response;
  },
};

Additional Resources