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.
Serve the Api from a Worker
Section titled “Serve the Api from a Worker”import * as Cloudflare from "alchemy/Cloudflare";import * as DomainCloudflare from "alchemy/Domain/Cloudflare";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.DurableObjects),);
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.DurableObjects 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/Cloudflare";import * as Effect from "effect/Effect";import BankWorker from "./src/BankWorker.ts";
export default Alchemy.Stack( "Bank", { providers: Cloudflare.providers(), state: Alchemy.localState() }, 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”bun 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/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({ target: CustomerId.make(customerId), name: customerId }).pipe( Effect.catchTag("AlreadyRegistered", () => Effect.void), ); // Print the balance now and after every change, in the background. yield* bank.AccountSummary.watch(accountId).pipe( Stream.filter((summary) => summary !== null && summary.owner !== null), Stream.runForEach((summary) => Effect.log(`balance: ${summary?.balance}`)), Effect.forkChild, ); yield* Effect.sleep("1 second"); yield* bank.DepositMoney({ target: accountId, amount: 100 }); yield* Effect.sleep("1 second"); yield* bank.WithdrawMoney({ target: accountId, amount: 30 }); yield* Effect.sleep("1 second");});
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, watches their balance in the background, then deposits and withdraws. The policy opens the account shortly after registration, so the watch waits for it to have an owner.
bun scripts/try.ts http://localhost:1337balance: 0balance: 100balance: 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”bun 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.