1---
2title: Layers
3description: A Layer defines a service contract with swappable implementations. In Alchemy, a Layer can encapsulate Resources and Bindings, so shared services carry their own infrastructure and permissions.
4---
5
6As your application grows, you'll need to start sharing components of
7your code using Layers. A
8[**Layer**](https://effect.website/docs/requirements-management/layers/)
9is Effect's unit of service construction: it defines how to build a
10service, so you can swap different implementations of that service in
11and out across your code — a different storage backend or a different
12cloud.
13
14In Alchemy, [Resources](/infrastructure-as-code/resource) and
15[Bindings](/infrastructure-as-effects/binding) can be encapsulated inside
16a Layer, meaning you can create shareable services that carry their own
17infrastructure and permissions. You can share these within your
18codebase, across packages, or publish them to npm for others to consume.
19
20Let's look at how they work.
21
22## The contract
23
24A service is a `Context.Service` — a typed tag that names a capability:
25
26```typescript
27import * as Alchemy from "alchemy";
28import * as Context from "effect/Context";
29import * as Effect from "effect/Effect";
30
31export class JobService extends Context.Service<
32 JobService,
33 {
34 getJob(id: string): Effect.Effect<Job, JobError, Alchemy.RuntimeContext>;
35 }
36>()("JobService") {}
37```
38
39Consumers write `yield* JobService` and get this typed object. The
40signature mentions `Alchemy.RuntimeContext` (covered below) and nothing
41cloud-specific.
42
43## The implementation
44
45`Layer.effect(JobService, …)` builds the service — "to construct
46`JobService`, run this Effect":
47
48```typescript
49import * as Effect from "effect/Effect";
50import * as Layer from "effect/Layer";
51
52export const JobServiceKV = Layer.effect(
53 JobService,
54 Effect.gen(function* () {
55 return {
56 getJob: Effect.fn(function* (id: string) {
57 // TODO: read the job from storage
58 }),
59 };
60 }),
61);
62```
63
64Declare the storage — a real KV namespace, owned by the Layer:
65
66```diff lang="typescript"
67+import * as Cloudflare from "alchemy/Cloudflare";
68 import * as Effect from "effect/Effect";
69 import * as Layer from "effect/Layer";
70
71 export const JobServiceKV = Layer.effect(
72 JobService,
73 Effect.gen(function* () {
74+ const MyKV = yield* Cloudflare.KV.Namespace("MyKV");
75+
76 return {
77 // ...
78 };
79 }),
80 );
81```
82
83Bind it — `kv` is a typed client for reading and writing that namespace
84at runtime:
85
86```diff lang="typescript"
87 Effect.gen(function* () {
88 const MyKV = yield* Cloudflare.KV.Namespace("MyKV");
89+ const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV);
90
91 return {
92 // ...
93 };
94 }),
95```
96
97Implement the contract against the client:
98
99```diff lang="typescript"
100 return {
101 getJob: Effect.fn(function* (id: string) {
102- // TODO: read the job from storage
103+ return yield* kv.get<Job>(id, "json");
104 }),
105 };
106```
107
108## The consumer
109
110A Worker yields the service and provides the Layer:
111
112```typescript
113export default Cloudflare.Worker(
114 "Api",
115 { main: import.meta.url },
116 Effect.gen(function* () {
117 const jobs = yield* JobService;
118
119 return {
120 fetch: Effect.gen(function* () {
121 return yield* jobs.getJob("job-1");
122 }),
123 };
124 }).pipe(
125 Effect.provide(
126 JobServiceKV.pipe(Layer.provide(Cloudflare.KV.ReadWriteNamespaceBinding)),
127 ),
128 ),
129);
130```
131
132Providing the Layer is what brings the infrastructure in. On the next
133deploy, the `MyKV` namespace is created in the
134[Stack](/infrastructure-as-code/stack) and bound to this Worker, so
135`kv.get` works inside its handlers.
136
137The inner `Layer.provide` satisfies `JobServiceKV`'s dependency on the KV
138runtime binding privately. The handler sees only `JobService`:
139
140```typescript
141JobServiceKV.pipe(Layer.provide(Cloudflare.KV.ReadWriteNamespaceBinding))
142```
143
144## Reuse across Workers
145
146A Layer is a value. Provide `JobServiceKV` to a second Worker and both
147share the same namespace, because `KV.Namespace("MyKV")` has a stable
148logical id:
149
150```typescript
151// src/Admin.ts
152export default Cloudflare.Worker(
153 "Admin",
154 { main: import.meta.url },
155 Effect.gen(function* () {
156 const jobs = yield* JobService;
157 // ...
158 }).pipe(
159 Effect.provide(
160 JobServiceKV.pipe(Layer.provide(Cloudflare.KV.ReadWriteNamespaceBinding)),
161 ),
162 ),
163);
164```
165
166The Stack ends up with one `MyKV` namespace, bound to both `Api` and
167`Admin`.
168
169## Swapping implementations
170
171Changing the implementation is a one-line change:
172
173```diff lang="typescript"
174- JobServiceKV.pipe(Layer.provide(Cloudflare.KV.ReadWriteNamespaceBinding)),
175+ JobServiceDynamo.pipe(Layer.provide(AWS.DynamoDB.GetItemHttp)),
176```
177
178This swap crossed clouds: the Worker still runs on Cloudflare, but its
179jobs now come from a DynamoDB table on AWS, with the credentials and
180permissions wired up automatically.
181
182Alchemy's own Bindings follow the same tag/Layer split
183([a contract and a Layer](/infrastructure-as-effects/binding#a-contract-and-a-layer)).
184
185## The types hold the boundary
186
187A Layer that needs an environment the host can't supply is a compile
188error:
189
190```typescript
191export default Cloudflare.Worker(
192 "Api",
193 { main: import.meta.url },
194 handler.pipe(Effect.provide(JobServiceDynamo)),
195 // ✗ Type error: the Layer requires AWS `Credentials` and `Region`,
196 // which a Cloudflare Worker can't provide
197);
198```
199
200And `Alchemy.RuntimeContext` marks methods that only work inside a
201deployed handler ([Phases](/infrastructure-as-effects/phases)):
202
203```typescript
204Effect.gen(function* () {
205 const jobs = yield* JobService;
206
207 yield* jobs.getJob("job-1");
208 // ✗ Type error: `RuntimeContext` only exists inside handlers
209
210 return {
211 fetch: Effect.gen(function* () {
212 return yield* jobs.getJob("job-1"); // ✓
213 }),
214 };
215});
216```
217
218## Composing Layers
219
220A typical app provides one Layer per capability:
221
222```typescript
223.pipe(
224 Effect.provide(Layer.mergeAll(
225 JobServiceKV, // provides JobService
226 BetterAuthD1, // provides BetterAuth
227 RateLimiterDurable, // provides RateLimiter
228 )),
229)
230```
231
232Each Layer brings its own resources into the Stack and its own typed
233service into scope. Use `Layer.mergeAll` for independent services and
234`Layer.provide` to satisfy one Layer's dependencies privately from
235another.
236
237## Where next
238
239- [Circular Bindings](/infrastructure-as-effects/circular-bindings) —
240 the tag/Layer split applied to two services that reference each other.
241- [Bindings](/infrastructure-as-effects/binding) — the deploy-time
242 mechanics Layers are built on.