# Layers from Config

Sometimes *which* implementation to build is itself a decision that requires
running an effect — reading an environment variable, querying a discovery
service, checking a feature flag. `Layer.unwrap` takes an
`Effect<Layer<...>>` and flattens it into a plain `Layer`. You compute the
layer inside an effect, then hand it back, and `unwrap` builds whatever you
returned.

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

export class MessageStoreError extends Schema.TaggedErrorClass<MessageStoreError>()(
  "MessageStoreError",
  { cause: Schema.Defect }
) {}

export class MessageStore extends Context.Service<MessageStore, {
  append(message: string): Effect.Effect<void>
  readonly all: Effect.Effect<ReadonlyArray<string>>
}>()("myapp/MessageStore") {
  // One concrete implementation: keep messages in memory.
  static readonly layerInMemory = Layer.effect(
    MessageStore,
    Effect.sync(() => {
      const messages: Array<string> = []
      return MessageStore.of({
        append: (message) => Effect.sync(() => { messages.push(message) }),
        all: Effect.sync(() => [...messages])
      })
    })
  )

  // Another, parameterised by a URL — pretend this opens a network connection.
  static readonly layerRemote = (url: URL) =>
    Layer.effect(
      MessageStore,
      Effect.try({
        try: () => {
          const messages: Array<string> = []
          return MessageStore.of({
            append: (message) =>
              Effect.sync(() => { messages.push(`[${url.host}] ${message}`) }),
            all: Effect.sync(() => [...messages])
          })
        },
        catch: (cause) => new MessageStoreError({ cause })
      })
    )

  // `Layer.unwrap` runs this effect once at build time and uses whichever layer
  // it returns. The decision logic lives in normal Effect code.
  static readonly layer = Layer.unwrap(
    Effect.gen(function*() {
      const useInMemory = yield* Config.boolean("MESSAGE_STORE_IN_MEMORY").pipe(
        Config.withDefault(false)
      )

      if (useInMemory) {
        return MessageStore.layerInMemory
      }

      // Reads MESSAGE_STORE_URL only when actually needed.
      const remoteUrl = yield* Config.url("MESSAGE_STORE_URL")
      return MessageStore.layerRemote(remoteUrl)
    })
  )
}
```

`MessageStore.layer` has the type of a regular layer — its callers don't know or
care that the choice was made dynamically. Provide it like any other layer:

```ts
import { Effect } from "effect"
import { MessageStore } from "./MessageStore.ts"

const program = Effect.gen(function*() {
  const store = yield* MessageStore
  yield* store.append("hello")
  return yield* store.all
})

Effect.runFork(program.pipe(Effect.provide(MessageStore.layer)))
```

## How `unwrap` works

- The effect you pass runs **once**, when the layer is built. Its errors and
  requirements become the resulting layer's errors and requirements — so reading
  config that fails surfaces as a layer build failure, not a silent fallback.
- The layer it returns is then built normally, with all the usual memoization
  and scoping. You can return any layer, including ones with their own
  dependencies.
- Because the decision is plain Effect code, you can branch on anything an effect
  can reach: configuration, another service, a clock, a remote lookup.

The type signature makes the propagation precise:

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

// Layer.unwrap: <A, E1, R1, E, R>(
//   self: Effect<Layer<A, E1, R1>, E, R>
// ) => Layer<A, E | E1, R1 | Exclude<R, Scope.Scope>>
```

The outer effect's error channel `E` is merged with the inner layer's error
channel `E1`, and the outer effect's requirements `R` are merged with the inner
layer's requirements `R1`. In the example above, the `Config.url` lookup can
fail, so `MessageStore.layer` carries a `ConfigError` in its error channel and a
`ConfigProvider` requirement — both inherited from the unwrapped effect.

## When to use it

Reach for `Layer.unwrap` whenever the *shape* of your dependency graph depends
on runtime information:

- **Environment switches** — in-memory vs. remote, stub vs. live, as above.
- **Config-driven wiring** — pick a database driver, a cache backend, or a
  region's endpoint based on configuration.
- **Feature flags** — enable an alternate implementation behind a flag read at
  startup.

If you only need to read configuration *into* a single fixed implementation,
you don't need `unwrap` — just `yield*` the config inside an ordinary
`Layer.effect`. Reach for `unwrap` specifically when the config decides *which
layer* to build. See [Configuration](https://effect.plants.sh/configuration/config/) for the full
Config API.

## Picking a driver from config

A common pattern: a single service has several concrete backends, and a config
value names which one to use. `Config.literals` restricts the value to a known
set and parses it for you, so the branch is exhaustive and typo-safe.

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

export class Cache extends Context.Service<Cache, {
  readonly get: (key: string) => Effect.Effect<string | undefined>
  readonly set: (key: string, value: string) => Effect.Effect<void>
}>()("myapp/Cache") {
  static readonly layerMemory = Layer.sync(Cache, () => {
    const store = new Map<string, string>()
    return Cache.of({
      get: (key) => Effect.sync(() => store.get(key)),
      set: (key, value) => Effect.sync(() => { store.set(key, value) })
    })
  })

  static readonly layerRedis = (url: URL) =>
    Layer.sync(Cache, () => {
      // imagine a real Redis client keyed off `url` here
      const store = new Map<string, string>()
      return Cache.of({
        get: (key) => Effect.sync(() => store.get(`${url.host}:${key}`)),
        set: (key, value) => Effect.sync(() => { store.set(`${url.host}:${key}`, value) })
      })
    })

  // CACHE_DRIVER is constrained to exactly "memory" | "redis".
  static readonly layer = Layer.unwrap(
    Effect.gen(function*() {
      const driver = yield* Config.literals(["memory", "redis"], "CACHE_DRIVER").pipe(
        Config.withDefault("memory" as const)
      )

      switch (driver) {
        case "memory":
          return Cache.layerMemory
        case "redis": {
          const url = yield* Config.url("CACHE_REDIS_URL")
          return Cache.layerRedis(url)
        }
      }
    })
  )
}
```

## Choosing Live vs. Test from a flag

The same idea swaps an entire implementation behind a boolean — handy for
running a real integration against live infrastructure or a fake in CI. Because
the unwrapped effect can read config, the toggle lives in one place.

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

export class Mailer extends Context.Service<Mailer, {
  readonly send: (to: string, body: string) => Effect.Effect<void>
}>()("myapp/Mailer") {
  // Talks to a real SMTP endpoint (sketched).
  static readonly Live = Layer.effect(
    Mailer,
    Effect.gen(function*() {
      const from = yield* Config.string("MAIL_FROM")
      return Mailer.of({
        send: (to, body) =>
          Effect.sync(() => { /* send via SMTP, from `from` */ void [from, to, body] })
      })
    })
  )

  // Records nothing, swallows everything — safe for tests.
  static readonly Test = Layer.succeed(Mailer)(
    Mailer.of({ send: () => Effect.void })
  )

  static readonly layer = Layer.unwrap(
    Effect.gen(function*() {
      const useLive = yield* Config.boolean("MAIL_LIVE").pipe(
        Config.withDefault(false)
      )
      return useLive ? Mailer.Live : Mailer.Test
    })
  )
}
```

## `Layer.unwrap` vs. `Layer.suspend`

Both defer building a layer, but they differ in *what* the deferral can see:

- **`Layer.unwrap(effect => Layer)`** defers behind an **effect**. The factory
  can read config, pull from other services, or perform any effectful work, and
  those requirements and errors flow into the resulting layer's `RIn` / `E`. Use
  it when the *choice* of layer is computed at build time.
- **`Layer.suspend(() => Layer)`** is a **pure** thunk — `() => Layer<A, E, R>`,
  no effect involved. It simply delays evaluating the factory until the layer is
  first built, then memoizes the result with normal layer sharing. The resulting
  layer's `A`, `E`, and `R` are exactly those of the returned layer; nothing is
  added.

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

class ApiUrl extends Context.Service<ApiUrl, string>()("ApiUrl") {}

// Effectful choice: needs ConfigProvider, can fail with ConfigError.
const fromConfig = Layer.unwrap(
  Effect.gen(function*() {
    const prod = yield* Config.boolean("PROD").pipe(Config.withDefault(false))
    return prod
      ? Layer.succeed(ApiUrl)("https://api.example.com")
      : Layer.succeed(ApiUrl)("http://localhost:3000")
  })
)

// Pure choice on a plain value: no requirements, no errors added.
const useProd = true
const fromValue = Layer.suspend(() =>
  useProd
    ? Layer.succeed(ApiUrl)("https://api.example.com")
    : Layer.succeed(ApiUrl)("http://localhost:3000")
)
```

If the only input to the decision is an ordinary value already in scope, prefer
`Layer.suspend` (or even a plain conditional). Reach for `Layer.unwrap` the
moment the decision needs to *run an effect*.

### Breaking layer reference cycles

`Layer.suspend` is also the tool for mutually-referential layers. Two layers
that mention each other in the same module would otherwise be a use-before-init
reference error; wrapping one in `suspend` delays touching the other until build
time, after both bindings exist.

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

class A extends Context.Service<A, string>()("A") {}
class B extends Context.Service<B, string>()("B") {}

// `bLayer` is referenced before it is defined; `suspend` defers the lookup.
const aLayer: Layer.Layer<A> = Layer.suspend(() =>
  Layer.succeed(A)("a").pipe(Layer.provideMerge(bLayer))
)
const bLayer: Layer.Layer<B> = Layer.succeed(B)("b")
```

## Reference

### `Layer.unwrap`

Flattens an `Effect<Layer<A, E1, R1>, E, R>` into a `Layer<A, E | E1, R1 | Exclude<R, Scope.Scope>>`.
The effect runs once at build time; its errors and requirements merge into the
resulting layer's. Use it when the choice of layer must be computed effectfully.

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

class Greeting extends Context.Service<Greeting, string>()("Greeting") {}

const choose = Effect.succeed(Layer.succeed(Greeting)("hello"))

const layer = Layer.unwrap(choose)
// => Layer<Greeting, never, never>  — the effect can't fail and needs nothing
```

### `Layer.suspend`

Builds a layer lazily from a pure factory `() => Layer<A, E, R>`. Evaluation is
deferred until first build and then memoized; the result type is exactly the
returned layer's. Use it for value-based choices and to break layer reference
cycles.

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

class Port extends Context.Service<Port, number>()("Port") {}

const layer = Layer.suspend(() => Layer.succeed(Port)(8080))
// => Layer<Port, never, never>
```

For the broader Config API used to drive these decisions, see
[Configuration](https://effect.plants.sh/configuration/config/); for composing, providing, and
memoizing the resulting layers, see [Managing Layers](https://effect.plants.sh/services-and-layers/managing-layers/).