Part 5: Deploy to Cloudflare
Put the bank on Cloudflare. Every account, customer, view and policy becomes a Durable Object, and a Worker serves the Api. You need a configured Cloudflare profile.
Install the Cloudflare provider
Section titled “Install the Cloudflare provider”pnpm add https://pkg.alchemy.run/@alchemy.run/cloudflare/pr:2930:51c662abun add https://pkg.alchemy.run/@alchemy.run/cloudflare/pr:2930:51c662anpm install https://pkg.alchemy.run/@alchemy.run/cloudflare/pr:2930:51c662ayarn add https://pkg.alchemy.run/@alchemy.run/cloudflare/pr:2930:51c662aServe the Api from a Worker
Section titled “Serve the Api from a Worker”import * as Cloudflare from "@alchemy.run/cloudflare";import * as DomainCloudflare from "@alchemy.run/cloudflare/Domain";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import { Bank, BankHandlers } from "./Bank.model.ts";import { BankApi } from "./BankApi.api.ts";import { SessionLive } from "./Session.middleware.ts";
const BankLive = SessionLive.pipe( Layer.provideMerge(Bank.layer(BankHandlers)), Layer.provide(DomainCloudflare.layerDurableObjects()),);
export default class BankWorker extends Cloudflare.Worker<BankWorker>()( "BankWorker", { main: import.meta.url }, Effect.gen(function* () { const api = yield* BankApi; return { fetch: api.fetch }; }).pipe(Effect.provide(BankLive)),) {}DomainCloudflare.layerDurableObjects() hosts the model: every aggregate, view and policy becomes a Durable Object class on this Worker, with no bindings to write. SessionLive is the middleware the Api’s entries declare. yield* BankApi serves the Api, and its fetch becomes the Worker’s.
Declare the stack
Section titled “Declare the stack”import * as Alchemy from "alchemy";import * as Cloudflare from "@alchemy.run/cloudflare";import * as Effect from "effect/Effect";import BankWorker from "./src/BankWorker.ts";
export default Alchemy.Stack( "Bank", { providers: Cloudflare.providers(), state: Cloudflare.state() }, Effect.gen(function* () { const bank = yield* BankWorker; return { url: bank.url.as<string>() }; }),);The stack deploys the Worker and returns its URL.
Run it locally
Section titled “Run it locally”pnpm alchemy devbun alchemy devnpx alchemy devyarn alchemy devalchemy dev runs the Worker and its Durable Objects on your machine and prints the URL, such as http://localhost:1337.
Call it
Section titled “Call it”import * as Domain from "@alchemy.run/domain";import * as Effect from "effect/Effect";import * as FetchHttpClient from "effect/http/FetchHttpClient";import * as Layer from "effect/Layer";import * as Stream from "effect/Stream";import { AccountId } from "../src/Account.id.ts";import { BankApi } from "../src/BankApi.api.ts";import { CustomerId } from "../src/Customer.id.ts";import { sessionClient } from "../src/Session.middleware.ts";
const [url, customerId = "c-1"] = process.argv.slice(2);const accountId = AccountId.make(`${customerId}-main`);
const program = Effect.gen(function* () { const bank = yield* BankApi.client; yield* bank.RegisterCustomer(CustomerId.make(customerId), { name: customerId }).pipe( Effect.catchTag("AlreadyRegistered", () => Effect.void), ); const summaries = bank.AccountSummary.watch(accountId).pipe( Stream.map(({ state }) => state), Stream.filter((summary) => summary !== null && summary.owner !== null), ); // The policy opens the account shortly after registration: wait for it. yield* Stream.runHead(summaries); // Print the balance now and after every change, in the background. yield* summaries.pipe( Stream.runForEach((summary) => Effect.log(`balance: ${summary?.balance}`)), Effect.forkChild, ); yield* Effect.sleep("1 second"); yield* bank.DepositMoney(accountId, { amount: 100 }); yield* Effect.sleep("1 second"); yield* bank.WithdrawMoney(accountId, { amount: 30 }); yield* Effect.sleep("2 seconds");});
const Connection = Layer.mergeAll(Domain.protocolHttp(url!), sessionClient(customerId)).pipe( Layer.provide(FetchHttpClient.layer),);
Effect.runPromise(program.pipe(Effect.scoped, Effect.provide(Connection)));The script registers a customer and waits for the policy to open their account, which happens shortly after registration. Then it watches the balance in the background while it deposits and withdraws.
bun scripts/try.ts http://localhost:1337[12:00:01.402] INFO (#9): balance: 0[12:00:02.613] INFO (#9): balance: 100[12:00:03.804] INFO (#9): balance: 70The watch prints the balance each time it changes. Over HTTP it checks for changes a few times a second, so the script pauses between commands to show each one; Part 6 switches to WebSockets, where the bank pushes every change.
Deploy
Section titled “Deploy”pnpm alchemy deploybun alchemy deploynpx alchemy deployyarn alchemy deployThe deploy prints the Worker’s url. Run the same script against it.
bun scripts/try.ts https://bank-bankworker-….workers.devThe bank now runs on Cloudflare, with one Durable Object per account.