Part 2: Add a Worker
In Part 1 you deployed an R2 Bucket. Now you’ll create a Cloudflare Worker that reads and writes objects in that bucket over HTTP.
Create the Worker file
Section titled “Create the Worker file”Create src/worker.ts. A Worker is a special kind of Resource — it
has both an infrastructure definition and a runtime implementation
expressed as an Effect.
import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import * as import HttpServerResponse
HttpServerResponse from "effect/unstable/http/HttpServerResponse";
export default import Cloudflare
Cloudflare.const Worker: <{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, never>(id: string, props: InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never> | Effect.Effect<InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never>, never, never>, impl: Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, never>) => Effect.Effect<...> (+3 overloads)
Worker( "Worker", { main: Input<string>
main: import.
The type of import.meta.
If you need to declare that a given property exists on import.meta,
this type may be augmented via interface merging.
meta.ImportMeta.path: string
Absolute path to the source file
path, }, import Effect
Effect.const gen: <never, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}>(f: () => Generator<never, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never>) => Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, never> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { return { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>
fetch: import Effect
Effect.const gen: <never, HttpServerResponse.HttpServerResponse>(f: () => Generator<never, HttpServerResponse.HttpServerResponse, never>) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, never> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text("Hello, world!"); }), }; }),);Bind the Bucket to the Worker
Section titled “Bind the Bucket to the Worker”Now let’s bind the R2 Bucket from Part 1 to our new Worker. The
problem is that the Bucket is declared inside the Stack’s generator
in alchemy.run.ts — we can’t import it from there.
A common pattern in Alchemy is to give each resource its own file.
Create src/bucket.ts:
import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";
export const const Bucket: Effect<Cloudflare.R2Bucket, never, Cloudflare.Providers>
Bucket = import Cloudflare
Cloudflare.const R2Bucket: (id: string, props?: { name?: Input<string | undefined>; storageClass?: Input<Cloudflare.R2Bucket.StorageClass | undefined>; jurisdiction?: Input<Cloudflare.R2Bucket.Jurisdiction | undefined>; locationHint?: Input<Cloudflare.R2Bucket.Location | undefined>;} | undefined) => Effect<Cloudflare.R2Bucket, never, Cloudflare.Providers> (+2 overloads)
R2Bucket("Bucket");Update alchemy.run.ts to import it instead of declaring it inline:
import * as import Alchemy
Alchemy from "alchemy";import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import { import Bucket
Bucket } from "./src/bucket.ts";
export default import Alchemy
Alchemy.Stack<{ bucketName: Alchemy.Output<string, never>;}, unknown>(stackName: string, options: Alchemy.StackProps<unknown>, eff: Effect.Effect<{ bucketName: Alchemy.Output<string, never>;}, never, unknown>): Effect.Effect<Alchemy.CompiledStack<{ bucketName: Alchemy.Output<string, never>;}, any>, never, never> (+2 overloads)export Stack
Stack( "MyApp", { StackProps<unknown>.providers: Layer<unknown, never, Alchemy.StackServices>
providers: import Cloudflare
Cloudflare.const providers: () => Layer<Cloudflare.Providers | Alchemy.Provider<Command> | Alchemy.Provider<Alchemy.Random> | Cloudflare.Credentials | Cloudflare.CloudflareEnvironment | Access | Retry, never, any>
Cloudflare providers, bindings, and credentials for Worker-based stacks.
providers(), StackProps<Req>.state: Layer<State, never, Alchemy.StackServices>
state: import Cloudflare
Cloudflare.function state(props?: { workerName?: string; noTrack?: boolean;}): Layer<...>export state
state(), }, import Effect
Effect.const gen: <any, { bucketName: Alchemy.Output<string, never>;}>(f: () => Generator<any, { bucketName: Alchemy.Output<string, never>;}, never>) => Effect.Effect<{ bucketName: Alchemy.Output<string, never>;}, unknown, unknown> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const bucket: Cloudflare.R2Bucket
bucket = yield* import Cloudflare
Cloudflare.const R2Bucket: (id: string, props?: { name?: Alchemy.Input<string | undefined>; storageClass?: Alchemy.Input<Cloudflare.R2Bucket.StorageClass | undefined>; jurisdiction?: Alchemy.Input<Cloudflare.R2Bucket.Jurisdiction | undefined>; locationHint?: Alchemy.Input<Cloudflare.R2Bucket.Location | undefined>;} | undefined) => Effect.Effect<Cloudflare.R2Bucket, never, Cloudflare.Providers> (+2 overloads)
R2Bucket("Bucket"); const const bucket: any
bucket = yield* import Bucket
Bucket;
return { bucketName: Alchemy.Output<string, never>
bucketName: const bucket: Cloudflare.R2Bucket
bucket.bucketName: Alchemy.Output<string, never>
bucketName, }; }),);Now the Worker can import Bucket and bind it in the Init phase:
import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import * as import HttpServerResponse
HttpServerResponse from "effect/unstable/http/HttpServerResponse";import { const Bucket: Effect.Effect<Cloudflare.R2Bucket, never, Cloudflare.Providers>
Bucket } from "./bucket.ts";
export default import Cloudflare
Cloudflare.const Worker: <{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, Cloudflare.WorkerServices | PlatformServices>(id: string, props: InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never> | Effect.Effect<InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never>, never, never>, impl: Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, Cloudflare.WorkerServices | PlatformServices>) => Effect.Effect<...> (+3 overloads)
Worker( "Worker", { main: Input<string>
main: import.
The type of import.meta.
If you need to declare that a given property exists on import.meta,
this type may be augmented via interface merging.
meta.ImportMeta.path: string
Absolute path to the source file
path, }, import Effect
Effect.const gen: <Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.Providers | Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}>(f: () => Generator<Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.Providers | Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () {Error ts(2345) ― const const bucket: Cloudflare.R2BucketClient
bucket = yield* import Cloudflare
Cloudflare.const R2Bucket: ResourceClassWithMethods<Cloudflare.R2Bucket, { readonly bind: (<Req = never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, Req>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding | Req>) & ((bucket: Cloudflare.R2Bucket) => Effect.Effect<any, any, any>);}>
A Cloudflare R2 object storage bucket with S3-compatible API.
R2 provides zero-egress-fee object storage. Create a bucket as a resource,
then bind it to a Worker to read and write objects at runtime.
R2Bucket.bind: <Cloudflare.Providers>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, Cloudflare.Providers>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.Providers | Cloudflare.R2BucketBinding> (+1 overload)
bind(const Bucket: Effect.Effect<Cloudflare.R2Bucket, never, Cloudflare.Providers>
Bucket);
return { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>
fetch: import Effect
Effect.const gen: <never, HttpServerResponse.HttpServerResponse>(f: () => Generator<never, HttpServerResponse.HttpServerResponse, never>) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, never> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text("Hello, world!"); }), }; }),);Provide the binding layer
Section titled “Provide the binding layer”The previous step showed a type error — R2Bucket.bind requires the
R2BucketBinding service. Fix it by piping the outer Effect through
R2BucketBindingLive:
import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import * as import HttpServerResponse
HttpServerResponse from "effect/unstable/http/HttpServerResponse";import { import Bucket
Bucket } from "./bucket.ts";
export default import Cloudflare
Cloudflare.const Worker: <{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(id: string, props: InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never> | Effect.Effect<InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never>, never, never>, impl: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)
Worker( "Worker", { main: Input<string>
main: import.
The type of import.meta.
If you need to declare that a given property exists on import.meta,
this type may be augmented via interface merging.
meta.ImportMeta.path: string
Absolute path to the source file
path }, import Effect
Effect.const gen: <Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}>(f: () => Generator<Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never>) => Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, Cloudflare.R2BucketBinding> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const bucket: Cloudflare.R2BucketClient
bucket = yield* import Cloudflare
Cloudflare.const R2Bucket: ResourceClassWithMethods<Cloudflare.R2Bucket, { readonly bind: (<Req = never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, Req>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding | Req>) & ((bucket: Cloudflare.R2Bucket) => Effect.Effect<any, any, any>);}>
A Cloudflare R2 object storage bucket with S3-compatible API.
R2 provides zero-egress-fee object storage. Create a bucket as a resource,
then bind it to a Worker to read and write objects at runtime.
R2Bucket.bind: <never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, never>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding> (+1 overload)
bind(import Bucket
Bucket);
return { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>
fetch: import Effect
Effect.const gen: <never, HttpServerResponse.HttpServerResponse>(f: () => Generator<never, HttpServerResponse.HttpServerResponse, never>) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, never> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text("Hello, world!"); }), }; }).Pipeable.pipe<Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, Cloudflare.R2BucketBinding>, Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;}, never, Cloudflare.R2BucketBinding>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe(import Effect
Effect.const provide: <Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(layer: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>, options?: { readonly local?: boolean | undefined;} | undefined) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+5 overloads)
Provides dependencies to an effect using layers or a context. Use options.local
to build the layer every time; by default, layers are shared between provide
calls.
provide(import Cloudflare
Cloudflare.const R2BucketBindingLive: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>
R2BucketBindingLive)),);R2BucketBindingLive tells the Worker runtime how to look up the
underlying R2 binding from the Cloudflare environment. Without it,
bind wouldn’t know where to find the bucket at runtime.
Add the PUT handler
Section titled “Add the PUT handler”Let’s replace the placeholder response with a PUT route that stores
objects in the bucket. Add HttpServerRequest to access the incoming
request:
import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import { const HttpServerRequest: Service<HttpServerRequest, HttpServerRequest>
HttpServerRequest } from "effect/unstable/http/HttpServerRequest";import * as import HttpServerResponse
HttpServerResponse from "effect/unstable/http/HttpServerResponse";import { import Bucket
Bucket } from "./bucket.ts";
export default import Cloudflare
Cloudflare.const Worker: <Cloudflare.WorkerShape, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(id: string, props: InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never> | Effect.Effect<InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never>, never, never>, impl: Effect.Effect<Cloudflare.WorkerShape, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<...>>) => Effect.Effect<...> (+3 overloads)
Worker( "Worker", { main: Input<string>
main: import.
The type of import.meta.
If you need to declare that a given property exists on import.meta,
this type may be augmented via interface merging.
meta.ImportMeta.path: string
Absolute path to the source file
path }, import Effect
Effect.const gen: <Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>;}>(f: () => Generator<Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const bucket: Cloudflare.R2BucketClient
bucket = yield* import Cloudflare
Cloudflare.const R2Bucket: ResourceClassWithMethods<Cloudflare.R2Bucket, { readonly bind: (<Req = never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, Req>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding | Req>) & ((bucket: Cloudflare.R2Bucket) => Effect.Effect<any, any, any>);}>
A Cloudflare R2 object storage bucket with S3-compatible API.
R2 provides zero-egress-fee object storage. Create a bucket as a resource,
then bind it to a Worker to read and write objects at runtime.
R2Bucket.bind: <never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, never>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding> (+1 overload)
bind(import Bucket
Bucket); return { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>
fetch: import Effect
Effect.const gen: <Service<HttpServerRequest, HttpServerRequest> | Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment>, HttpServerResponse.HttpServerResponse>(f: () => Generator<Service<HttpServerRequest, HttpServerRequest> | Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment>, HttpServerResponse.HttpServerResponse, never>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const request: HttpServerRequest
request = yield* const HttpServerRequest: Service<HttpServerRequest, HttpServerRequest>
HttpServerRequest; const const key: string
key = const request: HttpServerRequest
request.HttpServerRequest.url: string
url.String.split(separator: string | RegExp, limit?: number): string[] (+1 overload)
Split a string into substrings using the specified separator and return them as an array.
split("/").Array<string>.pop(): string | undefined
Removes the last element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
pop()!; if (const request: HttpServerRequest
request.HttpServerRequest.method: HttpMethod
method === "PUT") { yield* const bucket: Cloudflare.R2BucketClient
bucket.R2BucketClient.put<HttpServerError>(key: string, value: string | ReadableStream<any> | ArrayBuffer | ArrayBufferView<ArrayBufferLike> | Blob | Stream<Uint8Array<ArrayBufferLike>, HttpServerError, never> | null, options: Cloudflare.R2PutOptions & { contentLength: number;}): Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment> (+2 overloads)
put(const key: string
key, const request: HttpServerRequest
request.HttpIncomingMessage<HttpServerError>.stream: Stream<Uint8Array<ArrayBufferLike>, HttpServerError, never>
stream, { contentLength: number
contentLength: var Number: NumberConstructor(value?: any) => number
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number(const request: HttpServerRequest
request.HttpIncomingMessage<HttpServerError>.headers: Headers
headers["content-length"] ?? 0), }); return import HttpServerResponse
HttpServerResponse.const empty: (options?: HttpServerResponse.Options.WithContent | undefined) => HttpServerResponse.HttpServerResponse
empty({ status?: number | undefined
status: 201 }); }
return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text("Hello, world!"); }), }; }).Pipeable.pipe<Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBinding>, Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<...>>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe(import Effect
Effect.const provide: <Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(layer: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>, options?: { readonly local?: boolean | undefined;} | undefined) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+5 overloads)
Provides dependencies to an effect using layers or a context. Use options.local
to build the layer every time; by default, layers are shared between provide
calls.
provide(import Cloudflare
Cloudflare.const R2BucketBindingLive: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>
R2BucketBindingLive)),);TypeScript flags a type error. The bucket.put call can fail with
R2Error, but a Worker’s fetch handler only allows
HttpServerError or HttpBodyError. Effect tracks this in the type
system — you can’t forget to handle it.
Handle R2 errors
Section titled “Handle R2 errors”Pipe the fetch Effect through Effect.catchTag to convert R2Error
into a 500 response:
import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import { const HttpServerRequest: Service<HttpServerRequest, HttpServerRequest>
HttpServerRequest } from "effect/unstable/http/HttpServerRequest";import * as import HttpServerResponse
HttpServerResponse from "effect/unstable/http/HttpServerResponse";import { import Bucket
Bucket } from "./bucket.ts";
export default import Cloudflare
Cloudflare.const Worker: <{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(id: string, props: InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never> | Effect.Effect<InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never>, never, never>, impl: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)
Worker( "Worker", { main: Input<string>
main: import.
The type of import.meta.
If you need to declare that a given property exists on import.meta,
this type may be augmented via interface merging.
meta.ImportMeta.path: string
Absolute path to the source file
path }, import Effect
Effect.const gen: <Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}>(f: () => Generator<Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const bucket: Cloudflare.R2BucketClient
bucket = yield* import Cloudflare
Cloudflare.const R2Bucket: ResourceClassWithMethods<Cloudflare.R2Bucket, { readonly bind: (<Req = never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, Req>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding | Req>) & ((bucket: Cloudflare.R2Bucket) => Effect.Effect<any, any, any>);}>
A Cloudflare R2 object storage bucket with S3-compatible API.
R2 provides zero-egress-fee object storage. Create a bucket as a resource,
then bind it to a Worker to read and write objects at runtime.
R2Bucket.bind: <never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, never>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding> (+1 overload)
bind(import Bucket
Bucket); return { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>
fetch: import Effect
Effect.const gen: <Service<HttpServerRequest, HttpServerRequest> | Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment>, HttpServerResponse.HttpServerResponse>(f: () => Generator<Service<HttpServerRequest, HttpServerRequest> | Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment>, HttpServerResponse.HttpServerResponse, never>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const request: HttpServerRequest
request = yield* const HttpServerRequest: Service<HttpServerRequest, HttpServerRequest>
HttpServerRequest; const const key: string
key = const request: HttpServerRequest
request.HttpServerRequest.url: string
url.String.split(separator: string | RegExp, limit?: number): string[] (+1 overload)
Split a string into substrings using the specified separator and return them as an array.
split("/").Array<string>.pop(): string | undefined
Removes the last element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
pop()!;
if (const request: HttpServerRequest
request.HttpServerRequest.method: HttpMethod
method === "PUT") { yield* const bucket: Cloudflare.R2BucketClient
bucket.R2BucketClient.put<HttpServerError>(key: string, value: string | ReadableStream<any> | ArrayBuffer | ArrayBufferView<ArrayBufferLike> | Blob | Stream<Uint8Array<ArrayBufferLike>, HttpServerError, never> | null, options: Cloudflare.R2PutOptions & { contentLength: number;}): Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment> (+2 overloads)
put(const key: string
key, const request: HttpServerRequest
request.HttpIncomingMessage<HttpServerError>.stream: Stream<Uint8Array<ArrayBufferLike>, HttpServerError, never>
stream, { contentLength: number
contentLength: var Number: NumberConstructor(value?: any) => number
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number(const request: HttpServerRequest
request.HttpIncomingMessage<HttpServerError>.headers: Headers
headers["content-length"] ?? 0), }); return import HttpServerResponse
HttpServerResponse.const empty: (options?: HttpServerResponse.Options.WithContent | undefined) => HttpServerResponse.HttpServerResponse
empty({ status?: number | undefined
status: 201 }); }
return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text("Hello, world!"); }).Pipeable.pipe<Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>, Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe( import Effect
Effect.const catchTag: <"R2Error", HttpServerError | Cloudflare.R2Error, HttpServerResponse.HttpServerResponse, never, never, never, HttpServerError, never>(k: "R2Error", f: (e: Cloudflare.R2Error) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>, orElse?: ((e: HttpServerError) => Effect.Effect<never, HttpServerError, never>) | undefined) => <A, R>(self: Effect.Effect<A, HttpServerError | Cloudflare.R2Error, R>) => Effect.Effect<...> (+1 overload)
Catches and handles specific errors by their _tag field, which is used as a
discriminator.
When to Use
catchTag is useful when your errors are tagged with a readonly _tag field
that identifies the error type. You can use this function to handle specific
error types by matching the _tag value. This allows for precise error
handling, ensuring that only specific errors are caught and handled.
The error type must have a readonly _tag field to use catchTag. This
field is used to identify and match errors.
catchTag("R2Error", (error: Cloudflare.R2Error
error) => import Effect
Effect.const succeed: <HttpServerResponse.HttpServerResponse>(value: HttpServerResponse.HttpServerResponse) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>
Creates an Effect that always succeeds with a given value.
When to Use
Use this function when you need an effect that completes successfully with a
specific value without any errors or external dependencies.
succeed( import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text(error: Cloudflare.R2Error
error.Error.message: string
message, { status?: number | undefined
status: 500 }), ), ), ), }; }).Pipeable.pipe<Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBinding>, Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<...>>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe(import Effect
Effect.const provide: <Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(layer: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>, options?: { readonly local?: boolean | undefined;} | undefined) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+5 overloads)
Provides dependencies to an effect using layers or a context. Use options.local
to build the layer every time; by default, layers are shared between provide
calls.
provide(import Cloudflare
Cloudflare.const R2BucketBindingLive: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>
R2BucketBindingLive)),);Effect.catchTag matches errors by their _tag field. If an R2
operation fails at runtime, the Worker returns a 500 instead of
crashing — and the type error disappears because R2Error is now
fully handled.
Add the GET handler
Section titled “Add the GET handler”Complete the fetch handler by reading objects from the bucket when the request isn’t a PUT:
import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import { const HttpServerRequest: Service<HttpServerRequest, HttpServerRequest>
HttpServerRequest } from "effect/unstable/http/HttpServerRequest";import * as import HttpServerResponse
HttpServerResponse from "effect/unstable/http/HttpServerResponse";import { import Bucket
Bucket } from "./bucket.ts";
export default import Cloudflare
Cloudflare.const Worker: <{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(id: string, props: InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never> | Effect.Effect<InputProps<Cloudflare.WorkerProps<any, Cloudflare.WorkerAssetsConfig | undefined>, never>, never, never>, impl: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)
Worker( "Worker", { main: Input<string>
main: import.
The type of import.meta.
If you need to declare that a given property exists on import.meta,
this type may be augmented via interface merging.
meta.ImportMeta.path: string
Absolute path to the source file
path }, import Effect
Effect.const gen: <Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}>(f: () => Generator<Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding>, { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const bucket: Cloudflare.R2BucketClient
bucket = yield* import Cloudflare
Cloudflare.const R2Bucket: ResourceClassWithMethods<Cloudflare.R2Bucket, { readonly bind: (<Req = never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, Req>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding | Req>) & ((bucket: Cloudflare.R2Bucket) => Effect.Effect<any, any, any>);}>
A Cloudflare R2 object storage bucket with S3-compatible API.
R2 provides zero-egress-fee object storage. Create a bucket as a resource,
then bind it to a Worker to read and write objects at runtime.
R2Bucket.bind: <never>(args_0: Cloudflare.R2Bucket | Effect.Effect<Cloudflare.R2Bucket, never, never>) => Effect.Effect<Cloudflare.R2BucketClient, never, Cloudflare.R2BucketBinding> (+1 overload)
bind(import Bucket
Bucket); return { fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>
fetch: import Effect
Effect.const gen: <Service<HttpServerRequest, HttpServerRequest> | Effect.Effect<Cloudflare.R2ObjectBody | null, Cloudflare.R2Error, Cloudflare.WorkerEnvironment> | Effect.Effect<string, Cloudflare.R2Error, never> | Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment>, HttpServerResponse.HttpServerResponse>(f: () => Generator<...>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const request: HttpServerRequest
request = yield* const HttpServerRequest: Service<HttpServerRequest, HttpServerRequest>
HttpServerRequest; const const key: string
key = const request: HttpServerRequest
request.HttpServerRequest.url: string
url.String.split(separator: string | RegExp, limit?: number): string[] (+1 overload)
Split a string into substrings using the specified separator and return them as an array.
split("/").Array<string>.pop(): string | undefined
Removes the last element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
pop()!;
if (const request: HttpServerRequest
request.HttpServerRequest.method: HttpMethod
method === "PUT") { yield* const bucket: Cloudflare.R2BucketClient
bucket.R2BucketClient.put<HttpServerError>(key: string, value: string | ReadableStream<any> | ArrayBuffer | ArrayBufferView<ArrayBufferLike> | Blob | Stream<Uint8Array<ArrayBufferLike>, HttpServerError, never> | null, options: Cloudflare.R2PutOptions & { contentLength: number;}): Effect.Effect<Cloudflare.R2Object, HttpServerError | Cloudflare.R2Error, Cloudflare.WorkerEnvironment> (+2 overloads)
put(const key: string
key, const request: HttpServerRequest
request.HttpIncomingMessage<HttpServerError>.stream: Stream<Uint8Array<ArrayBufferLike>, HttpServerError, never>
stream, { contentLength: number
contentLength: var Number: NumberConstructor(value?: any) => number
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number(const request: HttpServerRequest
request.HttpIncomingMessage<HttpServerError>.headers: Headers
headers["content-length"] ?? 0), }); return import HttpServerResponse
HttpServerResponse.const empty: (options?: HttpServerResponse.Options.WithContent | undefined) => HttpServerResponse.HttpServerResponse
empty({ status?: number | undefined
status: 201 }); }
const const object: Cloudflare.R2ObjectBody | null
object = yield* const bucket: Cloudflare.R2BucketClient
bucket.R2BucketClient.get(key: string, options?: Cloudflare.R2GetOptions): Effect.Effect<Cloudflare.R2ObjectBody | null, Cloudflare.R2Error, Cloudflare.WorkerEnvironment> (+1 overload)
get(const key: string
key); if (const object: Cloudflare.R2ObjectBody | null
object === null) { return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text("Not found", { status?: number | undefined
status: 404 }); } const const text: string
text = yield* const object: Cloudflare.R2ObjectBody
object.R2ObjectBody.text(): Effect.Effect<string, Cloudflare.R2Error>
text(); return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text(const text: string
text); return import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text("Hello, world!"); }).Pipeable.pipe<Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>, Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError | Cloudflare.R2Error, HttpServerRequest | Cloudflare.WorkerEnvironment>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe( import Effect
Effect.const catchTag: <"R2Error", HttpServerError | Cloudflare.R2Error, HttpServerResponse.HttpServerResponse, never, never, never, HttpServerError, never>(k: "R2Error", f: (e: Cloudflare.R2Error) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>, orElse?: ((e: HttpServerError) => Effect.Effect<never, HttpServerError, never>) | undefined) => <A, R>(self: Effect.Effect<A, HttpServerError | Cloudflare.R2Error, R>) => Effect.Effect<...> (+1 overload)
Catches and handles specific errors by their _tag field, which is used as a
discriminator.
When to Use
catchTag is useful when your errors are tagged with a readonly _tag field
that identifies the error type. You can use this function to handle specific
error types by matching the _tag value. This allows for precise error
handling, ensuring that only specific errors are caught and handled.
The error type must have a readonly _tag field to use catchTag. This
field is used to identify and match errors.
catchTag("R2Error", (error: Cloudflare.R2Error
error) => import Effect
Effect.const succeed: <HttpServerResponse.HttpServerResponse>(value: HttpServerResponse.HttpServerResponse) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>
Creates an Effect that always succeeds with a given value.
When to Use
Use this function when you need an effect that completes successfully with a
specific value without any errors or external dependencies.
succeed( import HttpServerResponse
HttpServerResponse.const text: (body: string, options?: HttpServerResponse.Options.WithContentType) => HttpServerResponse.HttpServerResponse
text(error: Cloudflare.R2Error
error.Error.message: string
message, { status?: number | undefined
status: 500 }), ), ), ), }; }).Pipeable.pipe<Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBinding>, Effect.Effect<{ fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest | Cloudflare.WorkerEnvironment>;}, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<...>>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe(import Effect
Effect.const provide: <Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>(layer: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>, options?: { readonly local?: boolean | undefined;} | undefined) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+5 overloads)
Provides dependencies to an effect using layers or a context. Use options.local
to build the layer every time; by default, layers are shared between provide
calls.
provide(import Cloudflare
Cloudflare.const R2BucketBindingLive: Layer<Cloudflare.R2BucketBinding, never, Cloudflare.R2BucketBindingPolicy | ExecutionContext<BaseExecutionContext>>
R2BucketBindingLive)),);The Worker now handles two routes:
PUT /:key— stores the request body in the bucketGET /:key— retrieves the object, returning 404 if missing
Because bucket.get also returns R2Error, the catchTag you added
in the previous step already covers it — no additional error handling
needed.
Wire the Worker into the Stack
Section titled “Wire the Worker into the Stack”Add the Worker to alchemy.run.ts and expose its URL as a stack
output:
import * as import Alchemy
Alchemy from "alchemy";import * as import Cloudflare
Cloudflare from "alchemy/Cloudflare";import * as import Effect
Effect from "effect/Effect";import { import Bucket
Bucket } from "./src/bucket.ts";import import Worker
Worker from "./src/worker.ts";
export default import Alchemy
Alchemy.Stack<{ bucketName: any; url: any;}, unknown>(stackName: string, options: Alchemy.StackProps<unknown>, eff: Effect.Effect<{ bucketName: any; url: any;}, never, unknown>): Effect.Effect<Alchemy.CompiledStack<{ bucketName: any; url: any;}, any>, never, never> (+2 overloads)export Stack
Stack( "MyApp", { StackProps<unknown>.providers: Layer<unknown, never, Alchemy.StackServices>
providers: import Cloudflare
Cloudflare.const providers: () => Layer<Cloudflare.Providers | Alchemy.Provider<Command> | Alchemy.Provider<Alchemy.Random> | Cloudflare.Credentials | Cloudflare.CloudflareEnvironment | Access | Retry, never, any>
Cloudflare providers, bindings, and credentials for Worker-based stacks.
providers(), StackProps<Req>.state: Layer<State, never, Alchemy.StackServices>
state: import Cloudflare
Cloudflare.function state(props?: { workerName?: string; noTrack?: boolean;}): Layer<...>export state
state(), }, import Effect
Effect.const gen: <any, { bucketName: any; url: any;}>(f: () => Generator<any, { bucketName: any; url: any;}, never>) => Effect.Effect<{ bucketName: any; url: any;}, unknown, unknown> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
gen(function* () { const const bucket: any
bucket = yield* import Bucket
Bucket; const const worker: any
worker = yield* import Worker
Worker;
return { bucketName: any
bucketName: const bucket: any
bucket.any
bucketName, url: any
url: const worker: any
worker.any
url, }; }),);Deploy
Section titled “Deploy”Deploy again. Alchemy detects the new Worker and the unchanged Bucket:
bun alchemy deploynpm run alchemy deploypnpm alchemy deployyarn alchemy deployPlan: 1 to create + Worker (Cloudflare.Worker) (1 bindings) + Bucket • Bucket (Cloudflare.R2Bucket) Proceed? ◉ Yes ○ No • Bucket (Cloudflare.R2Bucket) no change ✓ Worker (Cloudflare.Worker) created • Uploading worker (14.20 KB) ... • Enabling workers.dev subdomain... { bucketName: "myapp-bucket-a1b2c3d4e5", url: "https://myapp-worker-dev-you-abc123.workers.dev", }
Try it out
Section titled “Try it out”Use curl to write and read an object:
# Store an objectcurl -X PUT https://myapp-worker-dev-you-abc123.workers.dev/hello.txt \ -d 'Hello, world!'
# Retrieve itcurl https://myapp-worker-dev-you-abc123.workers.dev/hello.txt# → Hello, world!You now have:
- A Cloudflare Worker with GET and PUT routes
- An R2 Bucket bound to the Worker
- Stack outputs showing both the bucket name and worker URL
In Part 3, you’ll learn about stages and state stores so multiple developers (and CI) can deploy isolated environments.