Skip to content

Multi-tenant platform with Cloudflare Workers - complete isolation using binding dispatcher pattern

Notifications You must be signed in to change notification settings

dinasaur404/bindings-rpc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Multi-Tenant Platform built with Cloudflare Workers

A complete multi-tenant platform that uses Workers for Platforms to extend D1, KV, R2, and other Cloudflare services to your end customers with complete isolation.

Key Components

  • 🚀 Dispatch Worker (my-binding-dispatcher/) - Central routing service that routes requests to their dedicated workers
  • 👤 Customer Workers (customer-worker-1/, customer-worker-2/) - Isolated worker instances per customer with dedicated KV and R2 storage instances

Benefits

Complete Isolation - Each customer has their own worker runtime and storage namespaces
Scalable - Automatically scales
Secure - No cross-customer data access
Extensible - Easy to extend other Cloudflare services (D1, Durable Objects, etc.)

Worker Isolation Pattern

Each customer worker runs the same UserWorker class with the same methods:

  • uploadFile(), getFile(), deleteFile() for R2 operations
  • setData(), getData(), deleteData() for KV operations
  • getStats(), listFiles(), listData() for analytics

The key difference is that every worker instance is bound to its own, isolated storage resources (KV namespaces, R2 bucket, etc).

// customer-worker-1 bindings
{
  "USER_KV": "customer-1-kv-namespace",
  "USER_R2": "customer-1-files-bucket"  
}

// customer-worker-2 bindings  
{
  "USER_KV": "customer-2-kv-namespace",
  "USER_R2": "customer-2-files-bucket"
}

When userWorker.setData('config', {theme: 'dark'}) is called:

  • Customer 1's worker → writes to customer-1-kv-namespace
  • Customer 2's worker → writes to customer-2-kv-namespace

Same code, but isolated execution environments.

Request Flow

  1. Client Request → Dispatcher receives request with X-User-ID header
  2. User Resolution → Dispatcher routes request to the worker instance
  3. Isolated Execution → Customer worker completes KV/R2 operation using the dedicated resources bound to that worker
  4. Response → Results returned through dispatcher to client
graph TB
    Client[Client]
    Dispatcher[Dispatcher<br/>]
    Worker1[Customer Worker 1<br/>]
    Worker2[Customer Worker 2<br/>]
    KV1[Customer 1 KV<br/>]
    R21[Customer 1 R2<br/>]
    KV2[Customer 2 KV<br/>]
    R22[Customer 2 R2<br/>]
    
    Client -->|X-User-ID: user1| Dispatcher
    Client -->|X-User-ID: user2| Dispatcher
    Dispatcher -->|Route user1| Worker1
    Dispatcher -->|Route user2| Worker2
    Worker1 --> KV1
    Worker1 --> R21
    Worker2 --> KV2
    Worker2 --> R22
    
    style Worker1 fill:#e1f5fe
    style Worker2 fill:#e1f5fe
    style KV1 fill:#f3e5f5
    style R21 fill:#f3e5f5
    style KV2 fill:#fff3e0
    style R22 fill:#fff3e0
Loading

Resource-based operations (defined in the user worker)

File Operations (R2)

  • POST /files - Upload files with form-data (key, file)
  • GET /files?key=filename - Download files
  • DELETE /files?key=filename - Delete files

Data Operations (KV)

  • POST /data - Store JSON data {key, value}
  • GET /data?key=mykey - Retrieve data
  • DELETE /data?key=mykey - Remove data

Analytics

  • GET /stats - Get customer usage statistics
  • GET / - Health check

Example Usage

# Upload a file for user1
curl -X POST https://your-dispatcher.workers.dev/files \
  -H "X-User-ID: user1" \
  -F "key=logo.png" \
  -F "[email protected]"

# Store data for user2
curl -X POST https://your-dispatcher.workers.dev/data \
  -H "X-User-ID: user2" \
  -H "Content-Type: application/json" \
  -d '{"key": "settings", "value": {"theme": "dark"}}'

# Get user1 statistics
curl https://your-dispatcher.workers.dev/stats \
  -H "X-User-ID: user1"

Quick Start

1. Deploy Customer Workers

# Deploy first customer worker
cd customer-worker-1
npm install
wrangler deploy

# Deploy second customer worker  
cd ../customer-worker-2
npm install
wrangler deploy

2. Configure Dispatcher

Update my-binding-dispatcher/wrangler.jsonc with your worker names:

{
  "dispatch_namespaces": [{
    "binding": "DISPATCHER",
    "namespace": "bindings",
    "experimental_remote": true
  }]
}

3. Deploy Dispatcher

cd my-binding-dispatcher
npm install
wrangler deploy

4. Test

# Upload a test file
curl -X POST https://your-dispatcher.workers.dev/files \
  -H "X-User-ID: test-user" \
  -F "key=test.txt" \
  -F "[email protected]"

Configuration

Customer Worker Configuration

Each customer worker needs:

  • Unique KV namespace ID
  • Unique R2 bucket name
  • Same binding names (USER_KV, USER_R2)

Example wrangler.jsonc:

{
  "name": "customer-worker-1",
  "kv_namespaces": [{
    "binding": "USER_KV",
    "id": "your-unique-kv-id"
  }],
  "r2_buckets": [{
    "binding": "USER_R2", 
    "bucket_name": "customer-1-files"
  }]
}

Extending the Platform

Adding D1 Database Support

  1. Add D1 binding to customer workers:
{
  "d1_databases": [{
    "binding": "USER_DB",
    "database_name": "customer-1-db",
    "database_id": "your-d1-id"
  }]
}
  1. Add database methods to customer worker:
async queryDatabase(sql, params) {
  return await this.env.USER_DB.prepare(sql).bind(...params).all();
}

Feel free to use this to build your own multi-tenant platforms!

About

Multi-tenant platform with Cloudflare Workers - complete isolation using binding dispatcher pattern

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •