Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
title: Tenant binding
pcx_content_type: how-to
sidebar:
order: 5
---

The tenant binding eliminates the need to configure individual bindings for each resource your customers use.

The tenant binding gives Workers dynamic access to all resources within their tenant. Instead of configuring individual bindings for each resource, a single `MY_RESOURCES` binding gives your Worker the ability to discover and access KV namespaces, D1 databases, R2 buckets, and other Workers within the same tenant.

Resources become available immediately after creation—your Worker can access newly created resources without redeployment.

## Configuration

Add a tenant binding to your Worker configuration:

```json
{
"main": "./src/worker.ts",
"bindings": [
{
"type": "tenant",
"binding": "MY_RESOURCES"
}
]
}
```

## Usage

The tenant binding supports two methods for each resource type:

- **`get(name: string)`** - Get a specific resource by name
- **`list()`** - List all resources of that type in the tenant

## Resource access

### KV namespaces

```javascript
// Get a specific KV namespace
const userStore = env.MY_RESOURCES.kv.get("user-store");
if (userStore) {
await userStore.put("key", "value");
const value = await userStore.get("key");
}

// List all KV namespaces
const allKV = await env.MY_RESOURCES.kv.list();
```

### D1 databases

```javascript
// Get a specific database
const userDB = env.MY_RESOURCES.d1.get("user-database");
if (userDB) {
const result = await userDB.prepare("SELECT * FROM users").all();
}

// List all databases
const allDB = await env.MY_RESOURCES.d1.list();
```

## Error handling

Always check if resources exist before using them:

```javascript
export default {
async fetch(request, env, ctx) {
const database = env.MY_RESOURCES.d1.get("user-database");
if (!database) {
return new Response("Database not configured", { status: 503 });
}

try {
const result = await database.prepare("SELECT COUNT(*) as count FROM users").first();
return Response.json({ userCount: result.count });
} catch (error) {
return new Response("Database error", { status: 500 });
}
},
};
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: Dispatch Worker
pcx_content_type: how-to
sidebar:
order: 3
---

The Dispatch Worker routes incoming requests to the appropriate tenant resources. They act as the entry point to your platform, handling request routing, tenant identification, and resource discovery. With the platform binding, dispatch workers can dynamically access any tenant and their resources.

## Platform Binding

The `platform` binding gives dispatch workers access to all tenants and their resources within your platform.

### Configuration

Configure your dispatch worker with a platform binding:

```json
{
"main": "./src/dispatch-worker.ts",
"bindings": [
{
"type": "platform",
"binding": "DISPATCHER"
}
]
}
```

### Basic Usage

```typescript
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Get a tenant by name
const tenant = env.DISPATCHER.get("acme-corp");
if (!tenant) {
return new Response("Tenant not found", { status: 404 });
}

// Get a worker within the tenant
const worker = await tenant.workers.get("api-handler");
if (!worker) {
return new Response("Worker not found", { status: 404 });
}

// Forward the request to the tenant's worker
return await worker.fetch(request);
},
};
```

## Request routing

### Subdomain-based routing

Route requests based on subdomain to identify the tenant:

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

// Map subdomain to tenant name
const tenantName = subdomain; // e.g., "acme-corp.yourplatform.com"

const tenant = env.DISPATCHER.get(tenantName);
if (!tenant) {
return new Response("Tenant not found", { status: 404 });
}

// Route to the tenant's main worker
const mainWorker = await tenant.workers.get("main");
if (mainWorker) {
return await mainWorker.fetch(request);
}

return new Response("Service unavailable", { status: 503 });
},
};
```


## Deployment

Deploy your dispatch worker to handle platform routing:

```bash
curl -X PUT \
"https://api.cloudflare.com/client/v4/accounts/{account-id}/platforms/production/dispatch-worker" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-d '{
"script": "export default { async fetch(request, env) { /* your dispatch logic */ } }",
"bindings": [
{
"type": "platform",
"binding": "DISPATCHER"
}
]
}'
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
---
title: Getting started
pcx_content_type: tutorial
sidebar:
order: 2
---

This guide walks you through setting up your first platform with tenants, deploying customer resources, and implementing request routing.

## Prerequisites

Before you begin, ensure you have:

- A Workers for Platforms subscription

## Step 1: Create a platform

Create a platform to contain all your tenants:

```bash
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/{account-id}/platforms" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-d '{
"name": "my-platform"
}'
```

## Step 2: Create your first tenant

Create a tenant for your first customer:

```bash
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/{account-id}/platforms/my-platform/tenants" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-d '{
"name": "acme-corp"
}'
```

## Step 3: Create a KV namespace in the tenant

Create a KV namespace for the customer to store data:

```bash
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/{account-id}/platforms/my-platform/tenant/acme-corp/kv/namespaces" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-d '{
"title": "user-cache"
}'
```

## Step 4: Create a tenant worker

Deploy a worker that accesses the KV namespace:

```bash
curl -X PUT \
"https://api.cloudflare.com/client/v4/accounts/{account-id}/platforms/my-platform/tenant/acme-corp/workers/api-worker" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-d '{
"script": "export default {\n async fetch(request, env, ctx) {\n const cache = env.TENANT.kv.get(\"user-cache\");\n await cache.put(\"last-visit\", new Date().toISOString());\n const lastVisit = await cache.get(\"last-visit\");\n return new Response(`Last visit: ${lastVisit}`);\n }\n};",
"bindings": [
{
"type": "tenant",
"binding": "TENANT"
}
]
}'
```

## Step 5: Create a dispatch worker

Create a dispatch worker to route requests to the appropriate tenant:

```bash
curl -X PUT \
"https://api.cloudflare.com/client/v4/accounts/{account-id}/platforms/my-platform/dispatch-worker" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-d '{
"script": "export default {\n async fetch(request, env) {\n const url = new URL(request.url);\n const subdomain = url.hostname.split(\".\")[0];\n \n const tenant = env.DISPATCHER.get(subdomain);\n if (!tenant) {\n return new Response(\"Tenant not found\", { status: 404 });\n }\n \n const worker = await tenant.workers.get(\"api-worker\");\n if (!worker) {\n return new Response(\"Service unavailable\", { status: 503 });\n }\n \n return await worker.fetch(request);\n }\n};",
"bindings": [
{
"type": "platform",
"binding": "DISPATCHER"
}
]
}'
```

## Step 6: Test your setup

Test routing through your dispatch worker:

```bash
# Assuming your dispatch worker is accessible and you've configured DNS
# to route acme-corp.{your-domain}.com to it

curl "https://acme-corp.{your-domain}.com/"

# Response: Shows the last visit timestamp
```

## Using the TypeScript SDK

For easier development, use the Cloudflare TypeScript SDK:

```bash
npm install cloudflare
```

```typescript
import Cloudflare from 'cloudflare';

const cloudflare = new Cloudflare({
apiToken: process.env.CLOUDFLARE_API_TOKEN,
});

// Get the platform
const platform = await cloudflare.platforms.get({ name: 'my-platform' });

// Create a tenant
const tenant = await platform.tenants.create({
name: 'new-customer',
environment_variables: {
CUSTOMER_NAME: 'New Customer Inc'
}
});

// Deploy worker
await tenant.workers.update({
name: 'hello-worker',
content: {
script: `
export default {
async fetch(request, env, ctx) {
const customerName = env.CUSTOMER_NAME || 'Unknown';
return new Response('Hello from ' + customerName + '!');
}
};
`,
bindings: [{ type: 'tenant', binding: 'TENANT' }]
}
});
```

## Next steps

Now that you have a basic platform running, explore these guides:

- [Tenant Bindings](/cloudflare-for-platforms/workers-for-platforms/tenant/bindings/) - Learn about dynamic resource access
- [Dispatch Workers](/cloudflare-for-platforms/workers-for-platforms/tenant/dispatch-workers/) - Advanced routing patterns
- [API Reference](/cloudflare-for-platforms/workers-for-platforms/tenant/api-reference/) - Complete API documentation
- [Migration Guide](/cloudflare-for-platforms/workers-for-platforms/tenant/migration/) - Move existing Workers to the tenant model
Loading
Loading