# Composing Layers

Real applications are graphs of services: a repository needs a database client,
the client needs a connection pool, the pool needs configuration. Layer
composition is how you wire that graph. The two essential operators are
`Layer.provide` — feed one layer's output into another's requirements and keep
it private — and `Layer.provideMerge` — do the same but *also* expose the
dependency.

```ts
import { Array, Config, Context, Effect, type Option, Layer, Schema } from "effect"
import { SqlClient, SqlError } from "effect/unstable/sql"
import { PgClient } from "@effect/sql-pg"

// A leaf layer: the Postgres client, built from configuration. It produces a
// SqlClient and may fail if config or the connection is bad.
const SqlClientLayer: Layer.Layer<
  PgClient.PgClient | SqlClient.SqlClient,
  Config.ConfigError | SqlError.SqlError
> = PgClient.layerConfig({
  url: Config.redacted("DATABASE_URL")
})

class UserRepoError extends Schema.TaggedErrorClass<UserRepoError>()(
  "UserRepoError",
  { reason: SqlError.SqlError }
) {}

export class UserRepo extends Context.Service<UserRepo, {
  findById(id: string): Effect.Effect<
    Option.Option<{ readonly id: string; readonly name: string }>,
    UserRepoError
  >
}>()("myapp/UserRepo") {
  // The "naked" layer: it produces UserRepo but still *requires* a SqlClient.
  // Note `SqlClient.SqlClient` in the third type-parameter slot.
  static readonly layerNoDeps: Layer.Layer<
    UserRepo,
    never,
    SqlClient.SqlClient
  > = Layer.effect(
    UserRepo,
    Effect.gen(function*() {
      const sql = yield* SqlClient.SqlClient

      const findById = Effect.fn("UserRepo.findById")(function*(id: string) {
        const rows = yield* sql<{ readonly id: string; readonly name: string }>`
          SELECT * FROM users WHERE id = ${id}
        `
        return Array.head(rows)
      }, Effect.mapError((reason) => new UserRepoError({ reason })))

      return UserRepo.of({ findById })
    })
  )

  // `Layer.provide` satisfies the SqlClient requirement *and hides it*. The
  // result exposes only UserRepo — callers can't reach the SqlClient.
  static readonly layer: Layer.Layer<
    UserRepo,
    Config.ConfigError | SqlError.SqlError
  > = this.layerNoDeps.pipe(Layer.provide(SqlClientLayer))

  // `Layer.provideMerge` satisfies the requirement *and re-exposes* it, so the
  // result provides both UserRepo and SqlClient.
  static readonly layerWithSql: Layer.Layer<
    UserRepo | SqlClient.SqlClient,
    Config.ConfigError | SqlError.SqlError
  > = this.layerNoDeps.pipe(Layer.provideMerge(SqlClientLayer))
}
```

## `provide` vs. `provideMerge`

Both feed `SqlClientLayer` into `layerNoDeps`, removing `SqlClient` from its
requirements. The difference is what the *result* exposes:

- **`Layer.provide(dep)`** — the dependency is an implementation detail. The
  output type drops the dependency (`UserRepo` only). This is the default: keep
  internals private so other parts of the app can't accidentally couple to your
  database client.
- **`Layer.provideMerge(dep)`** — the dependency is part of your public surface.
  The output type unions both (`UserRepo | SqlClient`). Use it when callers
  legitimately need both — for example a health-check that pings the database
  directly while also using the repository.
**Tip:** Prefer `provide` unless you have a concrete reason to leak the dependency.
Smaller exposed surfaces make layers easier to swap in tests.

## Merging independent layers

When several layers don't depend on one another, combine them into a single
application layer with `Layer.mergeAll`. The result provides the union of every
service and requires the union of their dependencies:

```ts
import { Layer } from "effect"
import { Database } from "./Database.ts"
import { Mailer } from "./Mailer.ts"
import { Metrics } from "./Metrics.ts"

// One layer that provides Database, Mailer, and Metrics together.
export const AppLayer = Layer.mergeAll(
  Database.layer,
  Mailer.layer,
  Metrics.layer
)
```

## Memoization

A layer included more than once in the graph is built **only once** by default,
and the same instance is shared. If `UserRepo.layer` and `OrderRepo.layer` both
provide `SqlClientLayer`, exactly one Postgres client is created and both repos
share it. This is what makes deep dependency graphs efficient — you don't have
to hoist shared dependencies by hand.

The unit of sharing is the **same `Layer` reference**. Two distinct `Layer`
values that happen to build the same service are *not* deduplicated — only
re-uses of one value are. Sharing is coordinated by a `MemoMap`: each build gets
one, and every node in the graph consults it before constructing a layer. See
[Managing Layers](https://effect.plants.sh/services-and-layers/managing-layers/) for how a build starts,
and the [Managed Runtime](https://effect.plants.sh/services-and-layers/managed-runtime/) page for sharing
a `MemoMap` across many runs.

If you genuinely need a *fresh* copy of a layer (a second, isolated connection
pool, say), wrap it in `Layer.fresh` to opt out of sharing.

```ts
import { Layer } from "effect"
import { SqlClientLayer } from "./sql.ts"

// A second, independent SqlClient instance, not shared with the memoized one.
const IsolatedSql = Layer.fresh(SqlClientLayer)
```

## Type-level helpers

When writing generic wrappers over layers, extract the three type parameters from
a `Layer` type with these helpers — no need to restate them:

- **`Layer.Success<L>`** — the services the layer provides (`ROut`).
- **`Layer.Error<L>`** — the construction failure type (`E`).
- **`Layer.Services<L>`** — the requirements the layer still needs (`RIn`).

```ts
import { Layer } from "effect"

declare const SomeLayer: Layer.Layer<{ a: 1 }, Error, { b: 2 }>

type Out = Layer.Success<typeof SomeLayer>      // => { a: 1 }
type Err = Layer.Error<typeof SomeLayer>        // => Error
type Deps = Layer.Services<typeof SomeLayer>    // => { b: 2 }
```

The `provide`, `provideMerge`, and `updateService` signatures use `NoInfer` on
the dependency/transform position. This keeps inference flowing from the layer
being composed into the dependency, so TypeScript reports a mismatch at the layer
you wrote rather than silently widening the dependency's type to fit.

Next: build a layer whose *implementation* is chosen at runtime with
[Layers from config](https://effect.plants.sh/services-and-layers/layer-from-config/).

## Combinator reference

Every combinator below is data-last and pipeable: `self.pipe(Layer.provide(dep))`
or `Layer.provide(self, dep)`. Each produces a *new* layer; nothing is mutated.

### `Layer.provide`

Feeds the output of `that` into the requirements of `self` and keeps it private —
the result drops the satisfied services from its requirements (`Exclude<RIn, ROut>`)
and does **not** re-expose them. Accepts a single layer or an array of layers.

```ts
import { Context, Effect, Layer } from "effect"

class Config extends Context.Service<Config, { readonly prefix: string }>()("Config") {}
class Greeter extends Context.Service<Greeter, {
  readonly greet: (name: string) => Effect.Effect<string>
}>()("Greeter") {}

const ConfigLayer = Layer.succeed(Config, { prefix: "Hello" })

const GreeterLayer = Layer.effect(Greeter, Effect.gen(function*() {
  const config = yield* Config
  return { greet: (name) => Effect.succeed(`${config.prefix}, ${name}`) }
})) // Layer<Greeter, never, Config>

// Array form: provide several deps at once. Result: Layer<Greeter, never, never>
const Wired = GreeterLayer.pipe(Layer.provide([ConfigLayer]))
// => Config is satisfied and hidden; only Greeter is exposed
```

### `Layer.provideMerge`

Like `provide`, but the dependency services are **also re-exposed** in the result
(`ROut | ROut2`). Reach for it when a caller legitimately needs both the built
service and the dependency used to build it.

```ts
import { Context, Effect, Layer } from "effect"

class Config extends Context.Service<Config, { readonly prefix: string }>()("Config") {}
class Greeter extends Context.Service<Greeter, {
  readonly greet: (name: string) => Effect.Effect<string>
}>()("Greeter") {}

const ConfigLayer = Layer.succeed(Config, { prefix: "Hi" })
const GreeterLayer = Layer.effect(Greeter, Effect.gen(function*() {
  const config = yield* Config
  return { greet: (name) => Effect.succeed(`${config.prefix} ${name}`) }
}))

const Both = GreeterLayer.pipe(Layer.provideMerge(ConfigLayer))
// => Layer<Greeter | Config, never, never> — both are reachable downstream
```

### `Layer.merge`

Combines two independent layers (or one layer with an array of layers) so the
result provides the union of their services. Unlike `provide`, neither layer
feeds the other — use it for siblings, not for a dependency relationship. Both
layers build concurrently.

```ts
import { Context, Effect, Layer } from "effect"

class Database extends Context.Service<Database, {
  readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class Logger extends Context.Service<Logger, {
  readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}

const dbLayer = Layer.succeed(Database, { query: (sql) => Effect.succeed("ok") })
const loggerLayer = Layer.succeed(Logger, { log: (msg) => Effect.void })

const merged = Layer.merge(dbLayer, loggerLayer)
// => Layer<Database | Logger, never, never>
```

### `Layer.mergeAll`

The variadic form of `merge`: pass any number of independent layers and get one
layer providing the union of all their services and requiring the union of all
their dependencies.

```ts
import { Context, Layer } from "effect"

class A extends Context.Service<A, { readonly a: number }>()("A") {}
class B extends Context.Service<B, { readonly b: number }>()("B") {}
class C extends Context.Service<C, { readonly c: number }>()("C") {}

const AppLayer = Layer.mergeAll(
  Layer.succeed(A, { a: 1 }),
  Layer.succeed(B, { b: 2 }),
  Layer.succeed(C, { c: 3 })
)
// => Layer<A | B | C, never, never>
```

### `Layer.flatMap`

Builds a follow-on layer from the *output* of this one. The callback receives the
built `Context.Context` of the source layer, so you can read a service (e.g. a
`Config`) and pick or construct the next layer based on its value.

```ts
import { Context, Layer } from "effect"

class Config extends Context.Service<Config, { readonly url: string }>()("Config") {}
class Client extends Context.Service<Client, { readonly url: string }>()("Client") {}

const ConfigLayer = Layer.succeed(Config, { url: "postgres://localhost" })

const ClientLayer = ConfigLayer.pipe(
  Layer.flatMap((context) => {
    const config = Context.get(context, Config) // read the built Config
    return Layer.succeed(Client, { url: config.url })
  })
)
// => Layer<Client, never, never> — Config was consumed to build Client
```

### `Layer.tap`

Runs an effect when the layer builds **successfully**, receiving the produced
context. The result is discarded and the original output is preserved — use it
for logging or metrics on startup.

```ts
import { Console, Context, Layer } from "effect"

class Server extends Context.Service<Server, { readonly port: number }>()("Server") {}

const ServerLayer = Layer.succeed(Server, { port: 3000 }).pipe(
  Layer.tap((context) =>
    Console.log(`listening on ${Context.get(context, Server).port}`)
  )
)
// => still Layer<Server>; logs "listening on 3000" once built
```

### `Layer.tapError`

Runs an effect when layer construction fails with a **typed error**, receiving
that error. The layer still fails afterward with the original error (unless your
callback itself fails, which adds to the error type).

```ts
import { Console, Context, Effect, Layer } from "effect"

class DbError extends Error { readonly _tag = "DbError" }
class Database extends Context.Service<Database, { readonly q: string }>()("Database") {}

const DatabaseLayer = Layer.effect(Database, Effect.fail(new DbError())).pipe(
  Layer.tapError((error) => Console.error(`db build failed: ${error._tag}`))
)
// => logs "db build failed: DbError", then re-fails with the same DbError
```

### `Layer.tapCause`

Like `tapError`, but the callback receives the full `Cause` — so it can inspect
typed failures, unexpected defects, and interruption. Useful for reporting that
must distinguish a real error from a crash.

```ts
import { Cause, Console, Context, Effect, Layer } from "effect"

class Database extends Context.Service<Database, { readonly q: string }>()("Database") {}

const DatabaseLayer = Layer.effect(Database, Effect.fail("boom")).pipe(
  Layer.tapCause((cause) => Console.error(Cause.pretty(cause)))
)
// => prints the rendered cause, then re-fails with the original cause
```

### `Layer.orDie`

Turns layer construction failures into **defects**, removing the error from the
type (`E` becomes `never`). Use only when a build failure is genuinely
unrecoverable and should crash the fiber rather than be handled.

```ts
import { Context, Effect, Layer } from "effect"

class ConfigError extends Error { readonly _tag = "ConfigError" }
class Config extends Context.Service<Config, { readonly url: string }>()("Config") {}

const ConfigLayer = Layer.effect(Config, Effect.fail(new ConfigError()))
const Reliable = ConfigLayer.pipe(Layer.orDie)
// => Layer<Config, never, never> — the ConfigError is now a defect, not typed
```

### `Layer.catch`

Recovers from **all** typed errors by switching to a fallback layer. The handler
receives the typed error and returns the replacement layer to build instead.

```ts
import { Context, Effect, Layer } from "effect"

class Config extends Context.Service<Config, { readonly url: string }>()("Config") {}

const primary = Layer.effect(Config, Effect.fail(new Error("no config")))
const fallback = Layer.succeed(Config, { url: "http://localhost" })

const recovered = primary.pipe(Layer.catch(() => fallback))
// => Layer<Config, never, never> — falls back when primary fails
```

### `Layer.catchTag`

Recovers from one or more **specific tagged errors**, leaving other error tags in
the result's error type. Pass a single tag string or an array of tags; the
handler receives the matched, narrowed error.

```ts
import { Context, Data, Effect, Layer } from "effect"

class ConfigError extends Data.TaggedError("ConfigError")<{}> {}
class Config extends Context.Service<Config, { readonly apiUrl: string }>()("Config") {}

const configLayer = Layer.effect(Config, Effect.fail(new ConfigError()))
const fallbackLayer = Layer.succeed(Config, { apiUrl: "http://localhost" })

const recovered = configLayer.pipe(
  Layer.catchTag("ConfigError", () => fallbackLayer)
)
// => recovers from ConfigError; any other tagged error stays in the type
```

### `Layer.catchCause`

Recovers from any failure cause by switching to a fallback layer, with access to
the full `Cause` (typed errors, defects, interruption). Finalizers for resources
acquired by the failed layer run before the fallback is acquired.

```ts
import { Context, Effect, Layer } from "effect"

class Database extends Context.Service<Database, {
  readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}

const primary = Layer.effect(Database, Effect.fail("primary unreachable"))

const withFallback = primary.pipe(
  Layer.catchCause(() =>
    Layer.succeed(Database, { query: (sql) => Effect.succeed(`memory: ${sql}`) })
  )
)
// => Layer<Database, never, never> — uses the in-memory fallback on any failure
```

### `Layer.updateService`

Transforms a service produced by a downstream layer: it reads the existing
service via its `Tag`, applies `f`, and replaces it with the result. The updated
service tag is added to the layer's requirements, so something upstream must
still provide the original.

```ts
import { Context, Layer } from "effect"

class Logger extends Context.Service<Logger, {
  readonly prefix: string
}>()("Logger") {}

const withLoudPrefix = <A, E, R>(layer: Layer.Layer<A, E, R>) =>
  layer.pipe(
    Layer.updateService(Logger, (logger) => ({
      ...logger,
      prefix: logger.prefix.toUpperCase()
    }))
  )
// => wraps a layer so the Logger it sees has an upper-cased prefix
```

### `Layer.fresh`

Opts a single layer out of memoization, so each use builds a separate instance.
Reach for it only when two parts of the app genuinely need isolated resources —
by default sharing is what you want.

```ts
import { Context, Effect, Layer, Ref } from "effect"

class Counter extends Context.Service<Counter, { readonly id: number }>()("Counter") {}

const program = Effect.gen(function*() {
  const nextId = yield* Ref.make(0)
  const counterLayer = Layer.effect(Counter, Effect.gen(function*() {
    return { id: yield* Ref.updateAndGet(nextId, (n) => n + 1) }
  }))

  // Each `Layer.fresh(counterLayer)` builds its own Counter with a new id.
  const a = Layer.fresh(counterLayer)
  const b = Layer.fresh(counterLayer)
  // => a and b produce different Counter ids; the bare counterLayer would share one
})
```

### `Layer.mock`

Builds a layer from a **partial** service implementation, for tests. You supply
only the members you exercise; any omitted `Effect`/`Stream`/`Channel`-returning
member becomes a value that dies with an `UnimplementedError` if it is ever used.

The partial shape is typed by `Layer.PartialEffectful<S>`: effectful members
(and functions returning them) are made optional, while plain data properties
stay **required** — so you can't forget non-effect fields. `mock` is curried:
`Layer.mock(Tag)(partial)` or `Layer.mock(Tag, partial)`.

```ts
import { Context, Effect, Layer } from "effect"

class UserService extends Context.Service<UserService, {
  readonly config: { readonly apiUrl: string } // required: plain data
  readonly getUser: (id: string) => Effect.Effect<{ id: string; name: string }>
  readonly deleteUser: (id: string) => Effect.Effect<void>
}>()("UserService") {}

const testLayer = Layer.mock(UserService, {
  config: { apiUrl: "https://test" }, // required by PartialEffectful
  getUser: (id) => Effect.succeed({ id, name: "Test User" })
  // deleteUser omitted -> calling it dies with "UnimplementedError"
})
// => Layer<UserService>; getUser works, deleteUser fails loudly if called
```