Skip to content
// EFFECT DAYS 2026

AlchemyCloudflareCluster

Deploys the Cloudflare cluster with Alchemy v2 ("Infrastructure as Effects").

make runs inside an Effect-native Cloudflare.Worker init program. It registers the four cluster Durable Object classes on the hosting Worker (bindings, class exports, and SQLite migrations are all owned by Alchemy), builds the cluster layer together with the user's handler layer into the isolate-lifetime scope, and returns a handle for wiring the built context into the Worker's handlers. The user never declares or re-exports Durable Object classes.

The Wrangler path (CloudflareDurableObjects + CloudflareCluster.layer) never imports this module; @alchemy.run/cloudflare is an optional peer dependency needed only here.

Example Declaring a cluster

import * as Cloudflare from "@alchemy.run/cloudflare"
import { Effect, Layer } from "effect"
import { make } from "@effect/platform-cloudflare/AlchemyCloudflareCluster"
import { CloudflareCluster } from "@effect/platform-cloudflare"
export default Cloudflare.Worker("MyApp", {
main: import.meta.url
}, Effect.gen(function*() {
const cluster = yield* make({
entities: [Counter],
layer: Layer.mergeAll(CounterLayer, MaintenanceLayer, NightlyReport)
})
yield* Cloudflare.Workers.cron("0 * * * *", cluster.wake("hourly-maintenance"))
// A ClusterCron fires only after a Cron Trigger wakes its seed singleton
yield* Cloudflare.Workers.cron(
"30 * * * *",
cluster.wake(CloudflareCluster.clusterCronSingletonName("nightly-report"))
)
return {
fetch: cluster.provide(handler)
}
}).pipe(Effect.provide(Cloudflare.Workers.CronEventSourceLive)))
4 exports Since v4.1.3 Source

Constructors

Since v4.1.3

Builds the Cloudflare cluster inside an Effect-native Alchemy Worker.

Details

Always registers all four Durable Object classes (entity, workflow, durable queue, singleton) on the hosting Worker, so Alchemy owns their bindings and SQLite migrations across deploys. At runtime it builds CloudflareCluster.layer merged with the user's handler layer into the isolate-lifetime Scope โ€” the Scope is never closed, so do not wrap the Worker init program in Effect.provide(layer) for cluster services.

The returned handle exposes provide for the Worker's handlers, wake for user-declared Cron Triggers (Cloudflare.Workers.cron(expr, cluster.wake("name"))), and the four native namespace bindings as escape hatches.

A ClusterCron fires only after a Cron Trigger wakes its seed singleton. Pass CloudflareCluster.clusterCronSingletonName(name) to wake for it.

Signature

declare const make: <ROut, E, RIn>(options: MakeOptions<ROut, E, RIn>) => Effect.Effect<Cluster<ROut | ClusterServices>, E, Cloudflare.Worker | Exclude<RIn, ClusterServices>>

Models

Cluster

interface
Since v4.1.3

The handle returned by AlchemyCloudflareCluster.make.

Gotchas

Namespace bindings and context are available only in the deployed runtime. Reading them during plan evaluation throws a diagnostic; provide and wake remain inert during planning.

Signature

interface Cluster<in R = never> {
readonly context: Context<R>;
readonly entityNamespace: DurableObjectNamespace;
readonly provide: <A, E, R2>(effect: Effect<A, E, R2>) => Effect<A, E, Exclude<R2, R>>;
readonly queueNamespace: DurableObjectNamespace;
readonly singletonNamespace: DurableObjectNamespace;
readonly wake: (name: string) => () => Effect<void>;
readonly workflowNamespace: DurableObjectNamespace;
}
Since v4.1.3

The services make builds on top of the user layer: the cluster Sharding service, the workflow engine, and the persisted queue factory.

Signature

type ClusterServices = Sharding | WorkflowEngine | PersistedQueueFactory

MakeOptions

interface
Since v4.1.3

The entity definitions and handler layer the cluster is built from.

Signature

interface MakeOptions<ROut, E, RIn> {
readonly entities: readonly Array<Entity<any, any>>;
readonly keepAliveHeartbeat?: Input;
readonly layer: Layer<ROut, E, RIn>;
}