Skip to content

Bindings Reference

Cloudwerk provides seamless access to all Cloudflare bindings. In route handlers, you can import bindings directly for a clean, ergonomic API.

The simplest way to access bindings in route handlers is to import them directly:

// app/api/posts/route.ts
import { DB, CACHE } from '@cloudwerk/core/bindings'
import { json } from '@cloudwerk/core'
export async function GET() {
const { results: posts } = await DB.prepare('SELECT * FROM posts').all()
return json(posts)
}

Run the type generator to enable TypeScript autocomplete for your bindings:

Terminal window
cloudwerk bindings generate-types

This creates .cloudwerk/types/ with type definitions for your bindings and updates your tsconfig.json to include them.

The @cloudwerk/core/bindings module provides:

ExportDescription
Named exports (e.g., DB, KV, BUCKET)Direct access to bindings by name
bindingsProxy object for dynamic access to any binding
getBinding<T>(name)Type-safe binding retrieval
hasBinding(name)Check if a binding exists
getBindingNames()List all available binding names
queuesTyped queue producers (see below)
servicesTyped service RPC proxy (see below)
durableObjectsTyped Durable Object namespaces (see below)
import { bindings, getBinding, hasBinding, getBindingNames } from '@cloudwerk/core/bindings'
export async function GET() {
// Dynamic access
const db = bindings.DB as D1Database
// Type-safe retrieval
const kv = getBinding<KVNamespace>('CACHE')
// Conditional access
if (hasBinding('ANALYTICS')) {
const analytics = getBinding<AnalyticsEngine>('ANALYTICS')
// Use analytics...
}
// List all bindings
const available = getBindingNames()
return json({ availableBindings: available })
}

In loader functions, access bindings via the context parameter:

export async function loader({ context }: LoaderArgs) {
// Access bindings via context
const db = context.db; // D1 Database
const kv = context.kv; // KV Namespace
const r2 = context.r2; // R2 Bucket
const env = context.env; // Environment variables
}

Cloudwerk provides a typed queues proxy for sending messages to queue consumers defined in app/queues/.

import { queues } from '@cloudwerk/core/bindings'
// Send to email queue (defined in app/queues/email.ts)
await queues.email.send({
subject: 'Welcome!',
body: 'Thanks for signing up.',
})
// Send with delay
const delayedMessage = { to: '[email protected]', subject: 'Reminder', body: 'Follow up' }
await queues.email.send(delayedMessage, { delaySeconds: 60 })
// Batch send
await queues.notifications.sendBatch([
{ userId: '1', type: 'email', message: 'Hello' },
{ userId: '2', type: 'push', message: 'Hello' },
])
import { queues, getQueue, hasQueue, getQueueNames } from '@cloudwerk/core/bindings'
// Type-safe queue retrieval
interface EmailMessage {
to: string
subject: string
body: string
}
const emailQueue = getQueue<EmailMessage>('email')
// Check if queue exists
if (hasQueue('analytics')) {
await queues.analytics.send({ event: 'pageview' })
}
// List all queues
const available = getQueueNames() // ['email', 'notifications', 'analytics']
interface Queue<T = unknown> {
send(message: T, options?: SendOptions): Promise<void>
sendBatch(messages: T[], options?: SendOptions): Promise<void>
}
interface SendOptions {
delaySeconds?: number // 0-43200 (max 12 hours)
contentType?: 'json' | 'text' | 'bytes' | 'v8'
}

Cloudwerk provides a typed services proxy for calling service methods defined in app/services/.

import { services } from '@cloudwerk/core/bindings'
// Call email service (defined in app/services/email/service.ts)
const result = await services.email.send({
subject: 'Hello',
body: '<h1>Welcome!</h1>',
})
// Call payment service
const checkout = await services.payments.createCheckout({
customerId: 'cus_123',
items: [{ priceId: 'price_123', quantity: 1 }],
successUrl: '/success',
cancelUrl: '/cancel',
})
import { services, getService, hasService, getServiceNames } from '@cloudwerk/core/bindings'
// Type-safe service retrieval
interface EmailService {
send(params: { to: string; subject: string; body: string }): Promise<{ success: boolean }>
}
const email = getService<EmailService>('email')
// Check if service exists
if (hasService('analytics')) {
await services.analytics.track({ event: 'signup' })
}
// List all services
const available = getServiceNames() // ['email', 'payments', 'cache']

The services proxy automatically routes calls based on the configured mode:

// Your code (identical for both modes)
await services.email.send({ to: '...' })
// Local mode: Direct function call (no latency)
// Extracted mode: RPC via Cloudflare service binding

Cloudwerk provides a typed durableObjects proxy for accessing Durable Objects defined in app/objects/.

import { durableObjects } from '@cloudwerk/core/bindings'
// Get a counter DO by name
const counter = durableObjects.counter.get('user-123')
// Call RPC methods directly
const count = await counter.increment(5)
const current = await counter.getCount()
await counter.reset()
// Get by unique ID
const uniqueId = durableObjects.counter.newUniqueId()
const newCounter = durableObjects.counter.getById(uniqueId)
import { durableObjects, getDurableObject, hasDurableObject } from '@cloudwerk/core/bindings'
// Type-safe DO retrieval
interface CounterDO {
increment(amount?: number): Promise<number>
getCount(): Promise<number>
}
const counterNs = getDurableObject<CounterDO>('counter')
// Check if DO exists
if (hasDurableObject('game')) {
const game = durableObjects.game.get('room-abc')
}
interface DurableObjectNamespaceProxy<T> {
get(name: string): T // Get by name (deterministic)
getById(id: DurableObjectId): T // Get by unique ID
newUniqueId(): DurableObjectId // Generate unique ID
idFromName(name: string): DurableObjectId
idFromString(hexId: string): DurableObjectId
}

Cloudflare D1 is a serverless SQLite database.

# wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-database-id"
// Query builder (recommended)
const users = await context.db
.selectFrom('users')
.where('status', '=', 'active')
.execute();
// Raw queries
const result = await context.env.DB
.prepare('SELECT * FROM users WHERE id = ?')
.bind(userId)
.first();
// Batch queries
const results = await context.env.DB.batch([
context.env.DB.prepare('SELECT * FROM users'),
context.env.DB.prepare('SELECT * FROM posts'),
]);
interface D1Database {
prepare(query: string): D1PreparedStatement;
batch<T>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;
run(query: string): Promise<D1ExecResult>;
}
interface D1PreparedStatement {
bind(...values: unknown[]): D1PreparedStatement;
first<T>(column?: string): Promise<T | null>;
all<T>(): Promise<D1Result<T>>;
run(): Promise<D1ExecResult>;
}

Cloudflare Workers KV provides key-value storage.

# wrangler.toml
[[kv_namespaces]]
binding = "KV"
id = "your-kv-id"
// Get value
const value = await context.kv.get('key');
const jsonValue = await context.kv.get('key', 'json');
const streamValue = await context.kv.get('key', 'stream');
// Set value
await context.kv.put('key', 'value');
await context.kv.put('key', JSON.stringify(data));
// With expiration
await context.kv.put('key', 'value', {
expirationTtl: 3600, // 1 hour in seconds
});
// With metadata
await context.kv.put('key', 'value', {
metadata: { createdAt: Date.now() },
});
// Delete
await context.kv.delete('key');
// List keys
const keys = await context.kv.list();
const prefixedKeys = await context.kv.list({ prefix: 'user:' });
interface KVNamespace {
get(key: string, type?: 'text'): Promise<string | null>;
get(key: string, type: 'json'): Promise<unknown | null>;
get(key: string, type: 'arrayBuffer'): Promise<ArrayBuffer | null>;
get(key: string, type: 'stream'): Promise<ReadableStream | null>;
put(key: string, value: string | ArrayBuffer | ReadableStream, options?: KVPutOptions): Promise<void>;
delete(key: string): Promise<void>;
list(options?: KVListOptions): Promise<KVListResult>;
}

Cloudflare R2 provides S3-compatible object storage.

# wrangler.toml
[[r2_buckets]]
binding = "R2"
bucket_name = "my-bucket"
// Get object
const object = await context.r2.get('path/to/file.txt');
if (object) {
const text = await object.text();
const arrayBuffer = await object.arrayBuffer();
const blob = await object.blob();
}
// Put object
await context.r2.put('path/to/file.txt', 'Hello, World!');
await context.r2.put('path/to/file.txt', fileStream, {
httpMetadata: {
contentType: 'text/plain',
},
});
// Delete object
await context.r2.delete('path/to/file.txt');
// List objects
const objects = await context.r2.list();
const prefixedObjects = await context.r2.list({
prefix: 'uploads/',
limit: 100,
});
// Head (metadata only)
const head = await context.r2.head('path/to/file.txt');
interface R2Bucket {
get(key: string): Promise<R2ObjectBody | null>;
put(key: string, value: ReadableStream | ArrayBuffer | string, options?: R2PutOptions): Promise<R2Object>;
delete(key: string): Promise<void>;
list(options?: R2ListOptions): Promise<R2Objects>;
head(key: string): Promise<R2Object | null>;
}

Access environment variables and secrets.

# wrangler.toml
[vars]
ENVIRONMENT = "production"
API_URL = "https://api.example.com"

Set secrets:

Terminal window
wrangler secret put API_KEY
// Access variables
const environment = context.env.ENVIRONMENT;
const apiUrl = context.env.API_URL;
const apiKey = context.env.API_KEY; // Secret

Vector database for AI applications.

# wrangler.toml
[[vectorize]]
binding = "VECTORIZE"
index_name = "my-index"
// Insert vectors
await context.env.VECTORIZE.insert([
{ id: 'doc-1', values: [0.1, 0.2, 0.3], metadata: { title: 'Doc 1' } },
]);
// Query vectors
const results = await context.env.VECTORIZE.query(queryVector, {
topK: 10,
filter: { category: 'tech' },
});

Cloudflare Workers AI for inference.

# wrangler.toml
[ai]
binding = "AI"
// Text generation
const response = await context.env.AI.run('@cf/meta/llama-2-7b-chat-int8', {
prompt: 'Hello!',
});
// Embeddings
const embeddings = await context.env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: 'Hello, world!',
});
// Image classification
const result = await context.env.AI.run('@cf/microsoft/resnet-50', {
image: imageArrayBuffer,
});