Skip to content
Effect Days 2026 Get your ticket

Layer

Builds and wires services for Effect applications.

A Layer<ROut, E, RIn> describes how to acquire one or more services, which services are required to build them, and which errors can occur during acquisition. Layers can manage scoped resources, memoize shared services, combine with other layers, provide services to effects or streams, and attach error handling, tracing, or lifecycle hooks.

55 exports Added in v2.0.0 Source

Constructors

effect

Added in v2.0.0 Source

Constructs a layer from an effect that produces a single service.

When to use

Use when you need to construct a Layer-provided service with an Effect, dependencies, or scoped resource acquisition.

Details

This allows you to create a Layer from an Effect that produces a service. The Effect is executed in the scope of the layer, allowing for proper resource management.

See

  • effectContext for effectfully providing multiple services
  • effectDiscard for running construction work without providing services

Signature

declare const effect: {
<I, S>(service: Key<I, S>): <E, R>(effect: Effect<S, E, R>) => Layer<I, E, Exclude<R, Scope>>;
<I, S, E, R>(service: Key<I, S>, effect: Effect<NoInfer<S>, E, R>): Layer<I, E, Exclude<R, Scope>>;
}

Example

(Creating a layer from an effect)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const layer = Layer.effect(Database,
Effect.sync(() => ({
query: (sql: string) => Effect.succeed(`Query: ${sql}`)
}))
)
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, layer)) // => "Query: SELECT 1"

Constructs a layer from an effect that produces all services in a Context.

When to use

Use when you need a Layer that effectfully constructs a Context with multiple services.

Details

This allows you to create a Layer from an effectful computation that returns multiple services. The Effect is executed in the scope of the layer.

See

  • effect for effectfully providing a single service

Signature

declare function effectContext<A, E, R>(effect: Effect<Context<A>, E, R>): Layer<A, E, Exclude<R, Scope>>

Example

(Creating a layer from an effectful context)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<
Database,
{ readonly query: (sql: string) => Effect.Effect<string> }
>()("Database") {}
const layer = Layer.effectContext(
Effect.succeed(Context.make(Database, {
query: (sql: string) => Effect.succeed(`Query: ${sql}`)
}))
)
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, layer)) // => "Query: SELECT 1"

Constructs a layer from an effect, discarding its value and providing no services.

When to use

Use when layer construction should run an Effect for its side effects while providing no services.

See

  • empty for a no-op layer that performs no construction work

Signature

declare function effectDiscard<X, E, R>(effect: Effect<X, E, R>): Layer<never, E, Exclude<R, Scope>>

Example

(Running an effect during layer construction)

import { Effect, Layer } from "effect"
const logs: Array<string> = []
const initLayer = Layer.effectDiscard(
Effect.sync(() => {
logs.push("Initializing application...")
})
)
Effect.runSync(Effect.scoped(Layer.build(initLayer)))
logs // => ["Initializing application..."]

empty

Added in v2.0.0 Source

An empty layer that provides no services, cannot fail, has no requirements, and performs no construction or finalization work.

When to use

Use as the no-op branch when conditionally composing layers.

See

  • effectDiscard for running an effect while providing no services

Signature

declare const empty: Layer<never>

Example

(Disabling optional lifecycle work)

import { Context, Effect, Layer, Option } from "effect"
const Service = Context.Service<string>("Service")
const context = Effect.runSync(Effect.scoped(Layer.build(Layer.empty)))
Context.getOption(context, Service) // => Option.none()

forkMemoMap

Added in v4.0.0 Source

Constructs a child MemoMap effectfully, allowing it to reuse layers already memoized in the parent while isolating any new layer allocations to the child map.

When to use

Use when a layer build should inherit already memoized layers from an existing MemoMap while keeping newly memoized layers out of the parent map.

See

Signature

declare function forkMemoMap(parent: MemoMap): Effect<MemoMap>

Constructs a child MemoMap synchronously, allowing it to reuse layers already memoized in the parent while isolating any new layer allocations to the child map.

When to use

Use to synchronously fork a memo map for manual layer building when child builds should see parent memoized layers without writing newly built layers back to the parent.

See

Signature

declare function forkMemoMapUnsafe(parent: MemoMap): MemoMap

fromBuild

Added in v4.0.0 Source

Constructs a Layer from a function that uses a MemoMap and Scope to build the layer.

Details

The function receives a MemoMap for memoization and a Scope for resource management. A child scope is created, and if the build fails, the child scope is closed.

Signature

declare function fromBuild<ROut, E, RIn>(build: (memoMap: MemoMap, scope: Scope) => Effect<Context<ROut>, E, RIn>): Layer<ROut, E, RIn>

Example

(Constructing a layer from a build function)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const databaseLayer = Layer.fromBuild(() =>
Effect.sync(() =>
Context.make(Database, {
query: (sql: string) => Effect.succeed("result")
})
)
)
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, databaseLayer)) // => "result"

Constructs a Layer from a function that uses a MemoMap and Scope to build the layer, with automatic memoization.

Details

This is similar to fromBuild but provides automatic memoization of the layer construction. The layer will be memoized based on the provided MemoMap.

Signature

declare function fromBuildMemo<ROut, E, RIn>(build: (memoMap: MemoMap, scope: Scope) => Effect<Context<ROut>, E, RIn>): Layer<ROut, E, RIn>

Example

(Memoizing layer construction)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const databaseLayer = Layer.fromBuildMemo(() =>
Effect.sync(() =>
Context.make(Database, {
query: (sql: string) => Effect.succeed("result")
})
)
)
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, databaseLayer)) // => "result"

makeMemoMap

Added in v2.0.0 Source

Constructs a MemoMap effectfully so it can be used to build additional layers.

Signature

declare const makeMemoMap: Effect<MemoMap>

Example

(Creating a memo map in an effect)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
// Create a memo map safely within an Effect
const program = Effect.gen(function*() {
const memoMap = yield* Layer.makeMemoMap
const scope = yield* Effect.scope
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const context = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
return Context.get(context, Database)
})
const database = Effect.runSync(Effect.scoped(program))
Effect.runSync(database.query("SELECT 1")) // => "result"

Constructs a MemoMap synchronously so it can be used to build additional layers.

Signature

declare function makeMemoMapUnsafe(): MemoMap

Example

(Creating a memo map unsafely)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
// Create a memo map for manual layer building
const program = Effect.gen(function*() {
const memoMap = Layer.makeMemoMapUnsafe()
const scope = yield* Effect.scope
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const context = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
return Context.get(context, Database)
})
const database = Effect.runSync(Effect.scoped(program))
Effect.runSync(database.query("SELECT 1")) // => "result"

succeed

Added in v2.0.0 Source

Constructs a layer that provides a single service from an already available value.

When to use

Use when you need a Layer that provides a service from an already constructed implementation without effectful acquisition.

See

  • sync for constructing layers from lazy values

Signature

declare const succeed: {
<I, S>(service: Key<I, S>): (resource: S) => Layer<I>;
<I, S>(service: Key<I, S>, resource: NoInfer<S>): Layer<I>;
}

Example

(Creating a layer from a service implementation)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const DatabaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Query result: ${sql}`))
})
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, DatabaseLayer)) // => "Query result: SELECT 1"

Constructs a layer that provides all services in an already available Context.

When to use

Use when you need a Layer built from an existing Context, including when you need to provide multiple services at once.

Details

This is a more general version of succeed that allows you to provide multiple services at once through a Context.

See

  • succeed for providing a single service from a value

Signature

declare function succeedContext<A>(context: Context<A>): Layer<A>

Example

(Providing multiple services from a context)

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 logs: Array<string> = []
const context = Context.make(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
}).pipe(
Context.add(Logger, {
log: (msg: string) => Effect.sync(() => logs.push(msg))
})
)
const layer = Layer.succeedContext(context)
const program = Logger.use((logger) => logger.log("ready"))
Effect.runSync(Effect.provide(program, layer))
logs // => ["ready"]

suspend

Added in v2.0.0 Source

Constructs a layer lazily using the specified factory.

Details

The factory is evaluated only when the suspended layer is first built, and the result is memoized with normal layer sharing semantics.

Signature

declare function suspend<A, E, R>(evaluate: LazyArg<Layer<A, E, R>>): Layer<A, E, R>

Example

(Choosing a layer lazily)

import { Context, Effect, Layer } from "effect"
class Config extends Context.Service<Config, string>()("Config") {}
const useProd = true
const layer = Layer.suspend(() =>
useProd
? Layer.succeed(Config, "https://api.example.com")
: Layer.succeed(Config, "http://localhost:3000")
)
Effect.runSync(Effect.provide(Config, layer)) // => "https://api.example.com"

sync

Added in v2.0.0 Source

Constructs a layer lazily that provides a single service.

When to use

Use when you need a Layer that provides one service whose value is created synchronously, but creation should be deferred until the layer is built.

Details

This is a lazy version of succeed where the service value is computed synchronously only when the layer is built.

See

  • succeed for constructing layers from static values

Signature

declare const sync: {
<I, S>(service: Key<I, S>): (evaluate: LazyArg<S>) => Layer<I>;
<I, S>(service: Key<I, S>, evaluate: LazyArg<NoInfer<S>>): Layer<I>;
}

Example

(Lazily providing a service)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const layer = Layer.sync(Database, () => ({
query: (sql: string) => Effect.succeed(`Query: ${sql}`)
}))
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, layer)) // => "Query: SELECT 1"

syncContext

Added in v2.0.0 Source

Constructs a layer lazily that provides all services in a Context.

When to use

Use when you need a Layer that creates multiple services synchronously but defers that work until the layer is built.

Details

This is a lazy version of succeedContext where the Context is computed synchronously only when the layer is built.

See

  • sync for lazily providing a single service
  • succeedContext for providing an already available context

Signature

declare function syncContext<A>(evaluate: LazyArg<Context<A>>): Layer<A>

Example

(Lazily providing a context)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const layer = Layer.syncContext(() =>
Context.make(Database, {
query: (sql: string) => Effect.succeed(`Query: ${sql}`)
})
)
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, layer)) // => "Query: SELECT 1"

Converting

launch

Added in v2.0.0 Source

Builds this layer and keeps it alive until the returned effect is interrupted.

When to use

Use when you model your entire application as a layer, such as an HTTP server.

Details

When the returned effect is interrupted, the layer scope is closed and all finalizers registered during layer acquisition are run.

Signature

declare function launch<RIn, E, ROut>(self: Layer<ROut, E, RIn>): Effect<never, E, RIn>

Example

(Launching an application layer)

import { Context, Deferred, Effect, Fiber, Layer, Ref } from "effect"
class HttpServer extends Context.Service<HttpServer, {
readonly port: number
}>()("HttpServer") {}
const program = Effect.gen(function*() {
const events = yield* Ref.make<Array<string>>([])
const started = yield* Deferred.make<void>()
const serverLayer = Layer.effect(HttpServer, Effect.gen(function*() {
yield* Ref.update(events, (events) => [...events, "Starting HTTP server..."])
yield* Deferred.succeed(started, undefined)
return { port: 3000 }
}))
const fiber = yield* Effect.forkChild(Layer.launch(serverLayer))
yield* Deferred.await(started)
yield* Fiber.interrupt(fiber)
return yield* Ref.get(events)
})
await Effect.runPromise(program) // => ["Starting HTTP server..."]

unwrap

Added in v4.0.0 Source

Unwraps a Layer from an Effect, flattening the nested structure.

When to use

Use when you have an Effect that produces a Layer and you want to use that layer directly.

Details

The resulting Layer will have the combined error and dependency types from both the outer Effect and the inner Layer.

Signature

declare function unwrap<A, E1, R1, E, R>(self: Effect<Layer<A, E1, R1>, E, R>): Layer<A, E1 | E, R1 | Exclude<R, Scope>>

Example

(Unwrapping an effectful layer)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const layerEffect = Effect.succeed(
Layer.succeed(Database, { query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result")) })
)
const unwrappedLayer = Layer.unwrap(layerEffect)
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, unwrappedLayer)) // => "result"

Destructors

build

Added in v2.0.0 Source

Builds a layer into a scoped value.

Signature

declare function build<RIn, E, ROut>(self: Layer<ROut, E, RIn>): Effect<Context<ROut>, E, Scope | RIn>

Example

(Building a layer into a context)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
// Build a layer to get its services
const program = Effect.gen(function*() {
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
// Build the layer into Context - automatically manages scope and memoization
const context = yield* Layer.build(dbLayer)
// Extract the specific service from the built layer
const database = Context.get(context, Database)
return yield* database.query("SELECT * FROM users")
})
Effect.runSync(Effect.scoped(program)) // => "result"

Builds a layer into an Effect value, using the specified MemoMap to memoize the layer construction.

Signature

declare const buildWithMemoMap: {
(memoMap: MemoMap, scope: Scope): <RIn, E, ROut>(self: Layer<ROut, E, RIn>) => Effect<Context<ROut>, E, RIn>;
<RIn, E, ROut>(self: Layer<ROut, E, RIn>, memoMap: MemoMap, scope: Scope): Effect<Context<ROut>, E, RIn>;
}

Example

(Building layers with an explicit memo map)

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 logs: Array<string> = []
// Build layers with explicit memoization control
const program = Effect.gen(function*() {
const memoMap = yield* Layer.makeMemoMap
const scope = yield* Effect.scope
// Build database layer with memoization
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const dbContext = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
// Build logger layer with same memoization (reuses memo if same layer)
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(msg)))
})
const loggerContext = yield* Layer.buildWithMemoMap(
loggerLayer,
memoMap,
scope
)
return {
database: Context.get(dbContext, Database),
logger: Context.get(loggerContext, Logger)
}
})
const services = Effect.runSync(Effect.scoped(program))
Effect.runSync(services.logger.log("ready"))
logs // => ["ready"]

Builds a layer using an explicit scope.

When to use

Use to control the lifetime of layer resources with a scope supplied by the caller.

Details

Resources created by the layer are released when the supplied scope is closed, unless a resource extends its own scope.

Signature

declare const buildWithScope: {
(scope: Scope): <RIn, E, ROut>(self: Layer<ROut, E, RIn>) => Effect<Context<ROut>, E, RIn>;
<RIn, E, ROut>(self: Layer<ROut, E, RIn>, scope: Scope): Effect<Context<ROut>, E, RIn>;
}

Example

(Building a layer with an explicit scope)

import { Context, Effect, Layer, Scope } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const logs: Array<string> = []
// Build a layer with explicit scope control
const program = Effect.gen(function*() {
const scope = yield* Effect.scope
const dbLayer = Layer.effect(Database, Effect.gen(function*() {
logs.push("Initializing database...")
yield* Scope.addFinalizer(
scope,
Effect.sync(() => logs.push("Database closed"))
)
return { query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`)) }
}))
// Build with specific scope - resources tied to this scope
const context = yield* Layer.buildWithScope(dbLayer, scope)
const database = Context.get(context, Database)
return yield* database.query("SELECT * FROM users")
// Database will be closed when scope is closed
})
Effect.runSync(Effect.scoped(program)) // => "Result: SELECT * FROM users"
logs // => ["Initializing database...", "Database closed"]

Error Handling

catchCause

Added in v4.0.0 Source

Recovers from any failure cause by switching to another layer.

When to use

Use when you need Layer recovery to inspect more than the typed error, such as defects or interruption information.

Details

The handler receives the full Cause of the failed layer, including typed errors, unexpected defects, and interruption information, and returns the fallback layer to build instead. Finalizers for resources acquired by the failed layer are still run before the fallback layer is acquired.

See

  • catchTag for recovering from specific tagged errors

Signature

declare const catchCause: {
<E, RIn2, E2, ROut2>(onError: (cause: Cause<E>) => Layer<ROut2, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut & ROut2, E2, RIn2 | RIn>;
<RIn, E, ROut, RIn2, E2, ROut22>(self: Layer<ROut, E, RIn>, onError: (cause: Cause<E>) => Layer<ROut22, E2, RIn2>): Layer<ROut & ROut22, E2, RIn | RIn2>;
}

Example

(Recovering from layer failures by cause)

import { Context, Data, Effect, Layer } from "effect"
class DatabaseError extends Data.TaggedError("DatabaseError")<{
message: string
}> {}
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const primaryDatabaseLayer = Layer.effect(Database,
Effect.fail(new DatabaseError({ message: "Primary DB unreachable" }))
)
const databaseWithFallback = primaryDatabaseLayer.pipe(
Layer.catchCause(() => {
return Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Memory: ${sql}`))
})
})
)
const program = Effect.gen(function*() {
const database = yield* Database
return yield* database.query("SELECT * FROM users")
}).pipe(
Effect.provide(databaseWithFallback)
)
await Effect.runPromise(program) // => "Memory: SELECT * FROM users"

catchTag

Added in v4.0.0 Source

Recovers from specific tagged errors.

When to use

Use when only some tagged Layer construction errors should be recovered.

See

  • catchCause for recovering with access to the full cause

Signature

declare const catchTag: {
<K extends string | readonly [Tags<E>, Tags<E>], E, RIn2, E2, ROut2>(k: K, f: (e: ExtractTag<NoInfer<E>, K extends readonly [string, string] ? K[number] : K>) => Layer<ROut2, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut & ROut2, E2 | Exclude<E, {
readonly _tag: K;
}>, RIn2 | RIn>;
<RIn, E, ROut, K extends string | readonly [Tags<E>, Tags<E>], RIn2, E2, ROut2>(self: Layer<ROut, E, RIn>, k: K, f: (e: ExtractTag<E, K extends readonly [string, string] ? K[number] : K>) => Layer<ROut2, E2, RIn2>): Layer<ROut & ROut2, E2 | Exclude<E, {
readonly _tag: K;
}>, RIn | RIn2>;
}

Example

(Recovering from tagged layer errors)

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)
)
const program = Config.useSync((config) => config.apiUrl)
Effect.runSync(Effect.provide(program, recovered)) // => "http://localhost"

orDie

Added in v2.0.0 Source

Converts layer construction failures into defects, removing them from the layer's error type.

Details

Use this only when failures should be treated as unrecoverable defects rather than typed errors that callers can handle.

Signature

declare function orDie<A, E, R>(self: Layer<A, E, R>): Layer<A, never, R>

Example

(Converting layer failures to defects)

import { Context, Data, Effect, Exit, Layer } from "effect"
class DatabaseError extends Data.TaggedError("DatabaseError")<{
message: string
}> {}
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
// Layer that can fail during construction
const error = new DatabaseError({ message: "Connection failed" })
const flakyDatabaseLayer = Layer.effect(
Database,
Effect.fail(error)
)
// Convert failures to fiber death - removes error from type
const reliableDatabaseLayer = flakyDatabaseLayer.pipe(Layer.orDie)
// Now the layer type is Layer<Database, never, never> - no error in type
const program = Effect.gen(function*() {
const database = yield* Database
return yield* database.query("SELECT * FROM users")
}).pipe(
Effect.provide(reliableDatabaseLayer)
)
Effect.runSync(Effect.exit(program)) // => Exit.die(error)

Guards

isLayer

Added in v2.0.0 Source

Returns true if the specified value is a Layer, false otherwise.

Signature

declare function isLayer(u: unknown): u is Layer<unknown, unknown, unknown>

Example

(Checking whether a value is a layer)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const notALayer = { someProperty: "value" }
Layer.isLayer(dbLayer) // => true
Layer.isLayer(notALayer) // => false

Layers

fresh

Added in v2.0.0 Source

Creates a fresh version of this layer that will not be shared.

When to use

Use when you need two parts of an application to receive separate instances of a resource, such as two independent client sessions.

Gotchas

Do not use it just to work around confusing composition. By default, sharing the same layer value is usually the desired behavior.

Signature

declare function fresh<A, E, R>(self: Layer<A, E, R>): Layer<A, E, R>

Example

(Creating non-shared layer instances)

import { Context, Effect, Layer, Ref } from "effect"
class Counter extends Context.Service<Counter, {
readonly id: number
}>()("Counter") {}
class Left extends Context.Service<Left, {
readonly counterId: number
}>()("Left") {}
class Right extends Context.Service<Right, {
readonly counterId: number
}>()("Right") {}
const leftLayer = Layer.effect(Left, Effect.gen(function*() {
const counter = yield* Counter
return { counterId: counter.id }
}))
const rightLayer = Layer.effect(Right, Effect.gen(function*() {
const counter = yield* Counter
return { counterId: counter.id }
}))
const compareIds = Effect.gen(function*() {
const left = yield* Left
const right = yield* Right
return left.counterId === right.counterId
})
const program = Effect.gen(function*() {
const nextId = yield* Ref.make(0)
const counterLayer = Layer.effect(Counter, Effect.gen(function*() {
const id = yield* Ref.updateAndGet(nextId, (n) => n + 1)
return { id }
}))
const shared = Layer.merge(
Layer.provide(leftLayer, counterLayer),
Layer.provide(rightLayer, counterLayer)
)
const sharedResult = yield* Effect.provide(compareIds, shared)
const freshCounterLayer = Layer.fresh(counterLayer)
const fresh = Layer.merge(
Layer.provide(leftLayer, freshCounterLayer),
Layer.provide(rightLayer, freshCounterLayer)
)
const freshResult = yield* Effect.provide(compareIds, fresh)
return { shared: sharedResult, fresh: freshResult }
})
await Effect.runPromise(program) // => { shared: true, fresh: false }

Models

Layer interface

Added in v2.0.0 Source

A Layer describes how to build one or more services for dependency injection.

When to use

Use to model construction of application services for dependency injection, especially when services have dependencies, can fail during construction, or need scoped setup and release.

Details

A Layer<ROut, E, RIn> represents ROut as the services this layer provides, E as the possible errors during layer construction, and RIn as the services this layer requires as dependencies.

Signature

interface Layer<in ROut, out E = never, out RIn = never> extends Variance<ROut, E, RIn>, Pipeable {
[ignoreSymbol]?: LayerUnifyIgnore;
[typeSymbol]?: unknown;
[unifySymbol]?: LayerUnify<Layer<ROut, E, RIn>>;
}

LayerUnify interface

Added in v4.0.0 Source

Type-level hook that allows Layer values to participate in Unify inference.

Details

This is used by Effect's pipe and unification machinery to preserve the provided services, error, and requirements of a Layer.

Signature

interface LayerUnify<A extends {
[typeSymbol]?: any;
}> {
Layer?: () => A[typeof typeSymbol] extends Layer<any, any, any> | _ ? Layer<Success<Extract<any[any], Any>>, Error<Extract<any[any], Any>>, Services<Extract<any[any], Any>>> : never;
}

LayerUnifyIgnore interface

Added in v4.0.0 Source

Type-level marker used by Unify for Layer types that should be ignored during unification.

Signature

interface LayerUnifyIgnore {}

MemoMap interface

Added in v2.0.0 Source

A MemoMap is used to memoize layer construction and ensure sharing of layers.

Details

The MemoMap prevents duplicate construction of the same layer instance, enabling efficient resource sharing across layer dependencies.

Signature

interface MemoMap {
readonly "~effect/Layer/MemoMap": "~effect/Layer/MemoMap";
readonly get: <RIn, E, ROut>(layer: Layer<ROut, E, RIn>, scope: Scope) => Effect<Context<ROut>, E, RIn> | undefined;
readonly getOrElseMemoize: <RIn, E, ROut>(layer: Layer<ROut, E, RIn>, scope: Scope, build: (memoMap: MemoMap, scope: Scope) => Effect<Context<ROut>, E, RIn>) => Effect<Context<ROut>, E, RIn>;
}

Example

(Sharing layer construction with a memo map)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
// Create a custom MemoMap for manual layer building
const program = Effect.gen(function*() {
const memoMap = yield* Layer.makeMemoMap
const scope = yield* Effect.scope
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const context = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
return Context.get(context, Database)
})
const database = Effect.runSync(Effect.scoped(program))
Effect.runSync(database.query("SELECT 1")) // => "result"

Variance interface

Added in v2.0.0 Source

The variance interface for Layer type parameters.

Signature

interface Variance<in ROut, out E, out RIn> {
readonly "~effect/Layer": {
readonly _E: Covariant<E>;
readonly _RIn: Covariant<RIn>;
readonly _ROut: Contravariant<ROut>;
};
}

Options

SpanOptions interface

Added in v4.0.0 Source

Represents options that can be used to control the behavior of spans created for layers.

When to use

Use to configure tracing metadata, stack trace capture, and onEnd finalization for spans created by Layer.span and Layer.withSpan during layer construction.

Details

Extends Tracer.SpanOptions with onEnd, which runs when the layer span ends as the layer scope closes.

See

  • span for creating a layer span
  • withSpan for wrapping layer construction in a span

Signature

interface SpanOptions extends SpanOptions {
readonly onEnd?: (span: Span, exit: Exit<unknown, unknown>) => Effect<void>;
}

Other

Signature

declare const catch: {
<E, RIn2, E2, ROut2>(onError: (error: E) => Layer<ROut2, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut & ROut2, E2, RIn2 | RIn>;
<RIn, E, ROut, RIn2, E2, ROut2>(self: Layer<ROut, E, RIn>, onError: (error: E) => Layer<ROut2, E2, RIn2>): Layer<ROut & ROut2, E2, RIn | RIn2>;
}

Providing Services

provide

Added in v2.0.0 Source

Feeds the output services of the dependency layer into the requirements of this layer, returning a layer that only provides the services from this layer.

When to use

Use when you need to hide an implementation dependency layer from callers.

Details

In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is built first and is used to satisfy the requirements of serviceLayer.

See

Signature

declare const provide: {
<RIn, E, ROut>(that: Layer<ROut, E, RIn>): <RIn2, E2, ROut2>(self: Layer<ROut2, E2, RIn2>) => Layer<ROut2, E | E2, RIn | Exclude<RIn2, ROut>>;
<Layers extends [Any, ...Array<Any>]>(that: Layers): <A, E, R>(self: Layer<A, E, R>) => Layer<A, E | Error<Layers[number]>, Services<Layers[number]> | Exclude<R, Success<Layers[number]>>>;
<RIn2, E2, ROut2, RIn, E, ROut>(self: Layer<ROut2, E2, RIn2>, that: Layer<ROut, E, RIn>): Layer<ROut2, E2 | E, RIn | Exclude<RIn2, ROut>>;
<A, E, R, Layers extends [Any, ...Array<Any>]>(self: Layer<A, E, R>, that: Layers): Layer<A, E | Error<Layers[number]>, Services<Layers[number]> | Exclude<R, Success<Layers[number]>>>;
}

Example

(Providing layer dependencies)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
Effect.runSync(program) // => { id: "123", name: "DB: SELECT * FROM users WHERE id = 123" }
logs // => ["[LOG] Looking up user 123"]

provideMerge

Added in v2.0.0 Source

Feeds the output services of the dependency layer into the requirements of this layer, returning a layer that provides both sets of services.

When to use

Use when you need to compose Layers while keeping both the constructed service and the dependency used to build it available.

Details

Prefer provide when the dependency should stay private.

See

  • provide for keeping dependency services private

Signature

declare const provideMerge: {
<RIn, E, ROut>(that: Layer<ROut, E, RIn>): <RIn2, E2, ROut2>(self: Layer<ROut2, E2, RIn2>) => Layer<ROut | ROut2, E | E2, RIn | Exclude<RIn2, ROut>>;
<Layers extends [Any, ...Array<Any>]>(that: Layers): <A, E, R>(self: Layer<A, E, R>) => Layer<A | Success<Layers[number]>, E | Error<Layers[number]>, Services<Layers[number]> | Exclude<R, Success<Layers[number]>>>;
<RIn2, E2, ROut2, RIn, E, ROut>(self: Layer<ROut2, E2, RIn2>, that: Layer<ROut, E, RIn>): Layer<ROut2 | ROut, E2 | E, RIn | Exclude<RIn2, ROut>>;
<A, E, R, Layers extends [Any, ...Array<Any>]>(self: Layer<A, E, R>, that: Layers): Layer<A | Success<Layers[number]>, E | Error<Layers[number]>, Services<Layers[number]> | Exclude<R, Success<Layers[number]>>>;
}

Example

(Providing dependencies while retaining services)

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") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies and merge all services together
const allServicesLayer = userServiceLayer.pipe(
Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now the resulting layer provides UserService, Database, AND Logger
const program = Effect.gen(function*() {
const userService = yield* UserService
const logger = yield* Logger // Still available!
const database = yield* Database // Still available!
const user = yield* userService.getUser("123")
yield* logger.log(`Found user: ${user.name}`)
return user
}).pipe(
Effect.provide(allServicesLayer)
)
Effect.runSync(program) // => { id: "123", name: "DB: SELECT * FROM users WHERE id = 123" }
logs // => ["[LOG] Looking up user 123", "[LOG] Found user: DB: SELECT * FROM users WHERE id = 123"]

updateService

Added in v3.13.0 Source

Updates a service in the context with a new implementation.

When to use

Use to adapt or extend a service's behavior during the creation of a layer.

Details

This function modifies the existing implementation of a service in the context. It retrieves the current service, applies the provided transformation function f, and replaces the old service with the transformed one.

Signature

declare const updateService: {
<I, A>(service: Key<I, A>, f: (a: NoInfer<A>) => A): <A1, E1, R1>(layer: Layer<A1, E1, R1>) => Layer<A1, E1, I | R1>;
<A1, E1, R1, I, A>(layer: Layer<A1, E1, R1>, service: Key<I, A>, f: (a: NoInfer<A>) => A): Layer<A1, E1, R1 | I>;
}

Sequencing

flatMap

Added in v2.0.0 Source

Constructs a layer dynamically based on the output of this layer.

Signature

declare const flatMap: {
<A, A2, E2, R2>(f: (context: Context<A>) => Layer<A2, E2, R2>): <E, R>(self: Layer<A, E, R>) => Layer<A2, E2 | E, R2 | R>;
<A, E, R, A2, E2, R2>(self: Layer<A, E, R>, f: (context: Context<A>) => Layer<A2, E2, R2>): Layer<A2, E | E2, R | R2>;
}

Example

(Creating services from layer output)

import { Context, Effect, Layer } from "effect"
class Config extends Context.Service<Config, {
readonly dbUrl: string
readonly logLevel: string
}>()("Config") {}
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 logs: Array<string> = []
// Base config layer
const configLayer = Layer.succeed(Config, {
dbUrl: "postgres://localhost:5432/mydb",
logLevel: "debug"
})
// Dynamically create services based on config
const dynamicServiceLayer = configLayer.pipe(
Layer.flatMap((context) => {
const config = Context.get(context, Config)
// Create database layer based on config
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) =>
Effect.succeed(
`Querying ${config.dbUrl}: ${sql}`
))
})
// Create logger layer based on config
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) =>
config.logLevel === "debug"
? Effect.sync(() => logs.push(`[DEBUG] ${msg}`))
: Effect.sync(() => logs.push(msg))
)
})
// Return combined layer
return Layer.mergeAll(dbLayer, loggerLayer)
})
)
// Use the dynamic services
const program = Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
yield* logger.log("Starting database query")
const result = yield* database.query("SELECT * FROM users")
return result
}).pipe(
Effect.provide(dynamicServiceLayer)
)
Effect.runSync(program) // => "Querying postgres://localhost:5432/mydb: SELECT * FROM users"
logs // => ["[DEBUG] Starting database query"]

tap

Added in v2.0.0 Source

Performs the specified effect if this layer succeeds.

When to use

Use to run an effectful observation after a layer has been built successfully, such as logging or metrics, without changing the services the layer provides.

Details

The callback receives the services produced by this layer. Its result is discarded, and the original layer output is preserved.

See

  • tapError for running an effect when layer construction fails with a typed error
  • tapCause for running an effect when layer construction fails with any cause

Signature

declare const tap: {
<ROut, XR, RIn2, E2, X>(f: (context: Context<XR>) => Effect<X, E2, RIn2>): <RIn, E>(self: Layer<ROut, E, RIn>) => Layer<ROut, E2 | E, RIn | Exclude<RIn2, Scope>>;
<RIn, E, ROut, XR, RIn2, E2, X>(self: Layer<ROut, E, RIn>, f: (context: Context<XR>) => Effect<X, E2, RIn2>): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope>>;
}

tapCause

Added in v4.0.0 Source

Performs the specified effect when this layer fails with any cause.

When to use

Use to run diagnostics or reporting when layer construction fails and the full Cause is needed.

Details

The callback receives the layer's Cause, so it can inspect typed errors, defects, and interruption information. If the callback succeeds, the layer fails again with the original cause; if the callback fails, that failure is added to the layer's error type.

See

  • tapError for observing only typed layer construction errors
  • catchCause for recovering from a layer construction failure by switching to another layer

Signature

declare const tapCause: {
<E, XE, RIn2, E2, X>(f: (cause: Cause<XE>) => Effect<X, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope>>;
<RIn, E, XE, ROut, RIn2, E2, X>(self: Layer<ROut, E, RIn>, f: (cause: Cause<XE>) => Effect<X, E2, RIn2>): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope>>;
}

tapError

Added in v2.0.0 Source

Performs the specified effect if this layer fails.

When to use

Use to run logging, metrics, or other effects when layer construction fails while preserving the original typed error.

Details

The callback receives the typed error. If the callback succeeds, the layer still fails with the original error; if the callback fails, that failure is added to the layer's error type.

See

  • tap for running an effect when layer construction succeeds
  • tapCause for inspecting the full failure cause, including defects and interruption

Signature

declare const tapError: {
<E, XE, RIn2, E2, X>(f: (e: XE) => Effect<X, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope>>;
<RIn, E, XE, ROut, RIn2, E2, X>(self: Layer<ROut, E, RIn>, f: (e: XE) => Effect<X, E2, RIn2>): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope>>;
}

Services

CurrentMemoMap

Added in v3.13.0 Source

Context service for the current MemoMap used in layer construction.

When to use

Use when building custom layer operations that need to access the current memoization map from the fiber context.

Details

This service wraps a MemoMap as a Context.Service, making it available for dependency injection during layer construction.

See

  • MemoMap the memoization map type wrapped by this service

Signature

declare class CurrentMemoMap extends Shape<"effect/Layer/CurrentMemoMap", MemoMap, this> {
constructor(_: never);
static forkOrCreate<Services>(self: Context<Services>): MemoMap;
}

Testing

mock

Added in v3.17.0 Source

Creates a mock layer for testing purposes. You can provide a partial implementation of the service. Any missing members that are Effects, Streams, Channels, or functions returning them will fail with an unimplemented defect when used.

Details

Missing members are represented by a value that can be used as an Effect, Stream, Channel, or as a function returning an Effect. This lets the mock preserve the shape of common service methods while still failing loudly when an unimplemented member is exercised.

Signature

declare const mock: {
<I, S extends object>(service: Key<I, S>): (implementation: Simplify<{ [K in string | number | symbol]: S[K] } & { [K in string | number | symbol]: S[K] }>) => Layer<I>;
<I, S extends object>(service: Key<I, S>, implementation: NoInfer<Simplify<{ [K in string | number | symbol]: S[K] } & { [K in string | number | symbol]: S[K] }>>): Layer<I>;
}

Example

(Mocking services for tests)

import { Context, Effect, Layer } from "effect"
class UserService extends Context.Service<UserService, {
readonly config: { apiUrl: string }
readonly getUser: (
id: string
) => Effect.Effect<{ id: string; name: string }, Error>
readonly deleteUser: (id: string) => Effect.Effect<void, Error>
readonly updateUser: (
id: string,
data: object
) => Effect.Effect<{ id: string; name: string }, Error>
}>()("UserService") {}
// Create a partial mock - only implement what you need for testing
const testUserLayer = Layer.mock(UserService, {
config: { apiUrl: "https://test-api.com" }, // Required - non-Effect property
getUser: (id: string) => Effect.succeed({ id, name: "Test User" }) // Mock implementation
// deleteUser and updateUser are omitted - will throw UnimplementedError if called
})
// Use in tests
const testProgram = Effect.gen(function*() {
const userService = yield* UserService
// This works - we provided an implementation
const user = yield* userService.getUser("123")
// This would throw - we didn't implement deleteUser
// yield* userService.deleteUser("123") // UnimplementedError
return user.name
}).pipe(
Effect.provide(testUserLayer)
)
Effect.runSync(testProgram) // => "Test User"

PartialEffectful type

Added in v3.17.0 Source

A utility type for creating partial mocks of services in testing.

When to use

Use to type partial test service implementations where only exercised effectful members are stubbed.

Details

This type makes Effect, Stream, and Channel values and functions returning them optional, while keeping non-effectful properties required. This allows you to provide only the methods you need to test while leaving others unimplemented.

See

  • mock for creating a mock layer from a partial service implementation

Signature

type PartialEffectful<A extends object> = Types.Simplify<{ [K in keyof A]: A[K] } & { [K in keyof A]: A[K] }>

Tracing

parentSpan

Added in v2.0.0 Source

Constructs a layer that provides an existing span as the current parent span.

Details

The supplied span is made available through Tracer.ParentSpan for layers that are built with this layer. This API does not create, end, or close the span; the caller remains responsible for the span's lifetime.

Signature

declare function parentSpan(span: AnySpan): Layer<ParentSpan>

Example

(Referencing an existing parent span)

import { Context, Effect, Layer, Tracer } from "effect"
class Database extends Context.Service<Database, {
readonly spanId: string
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
// Create a layer that uses an existing span as parent
const databaseLayer = Layer.effect(
Database,
Effect.gen(function*() {
const parentSpan = yield* Effect.currentParentSpan
return {
spanId: parentSpan.spanId,
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
}
})
).pipe(Layer.provide(Layer.parentSpan(Tracer.externalSpan({
spanId: "42",
traceId: "000"
}))))
const program = Database.use((database) =>
Effect.map(database.query("SELECT 1"), (result) => ({ spanId: database.spanId, result })))
Effect.runSync(Effect.provide(program, databaseLayer)) // => { spanId: "42", result: "Result: SELECT 1" }

span

Added in v2.0.0 Source

Constructs a new Layer which creates a span and registers it as the current parent span.

Details

This allows you to create a traced scope for layer construction, making all operations within the layer constructor part of the same trace span. The span is automatically ended when the layer's scope is closed. If onEnd is provided, it receives the span and the layer scope's exit value when the span ends.

Signature

declare function span(name: string, options?: SpanOptions): Layer<ParentSpan>

Example

(Tracing layer construction with a span)

import { Context, Effect, Layer } from "effect"
import type { Tracer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const logs: Array<string> = []
// Create a traced layer - all operations performed during construction of
// the `Database` service are part of the "database-init" span
const databaseLayer = Layer.effect(Database, Effect.gen(function*() {
// These operations are traced under "database-init" span
logs.push("Connecting to database")
logs.push("Database connected")
const parentSpan = yield* Effect.currentParentSpan
logs.push((parentSpan as Tracer.Span).name)
return {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
}
})).pipe(Layer.provide(Layer.span("database-init", {
onEnd: (span, exit) =>
Effect.sync(() => logs.push(`Span ${span.name} ended with: ${exit._tag}`))
})))
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, databaseLayer)) // => "Result: SELECT 1"
logs // => ["Connecting to database", "Database connected", "database-init", "Span database-init ended with: Success"]

Wraps a layer so spans created during its construction use the supplied span as their parent.

Details

Use this to attach layer construction to an existing trace hierarchy. This API does not create or end the supplied parent span.

When the supplied span is a native Span, layer construction also receives diagnostic information that helps associate failures with the layer call site. External spans are only installed as the parent span and do not add this diagnostic call-site information.

Signature

declare const withParentSpan: {
(span: AnySpan, options?: TraceOptions): <A, E, R>(self: Layer<A, E, R>) => Layer<A, E, Exclude<R, ParentSpan>>;
<A, E, R>(self: Layer<A, E, R>, span: AnySpan, options?: TraceOptions): Layer<A, E, Exclude<R, ParentSpan>>;
}

Example

(Attaching layers to an existing parent span)

import { Context, Effect, Layer, Tracer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class Cache extends Context.Service<Cache, {
readonly get: (key: string) => Effect.Effect<string | null>
}>()("Cache") {}
// Create layers
const DatabaseLayer = Layer.effect(Database, Effect.gen(function*() {
return {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
}
}))
const CacheLayer = Layer.effect(Cache, Effect.gen(function*() {
return {
get: Effect.fn("Cache.get")((key: string) => Effect.succeed(`Cache: ${key}`))
}
}))
// Use with an existing parent span from Effect.withSpan
const program = Effect.withSpan("application-startup")(
Effect.gen(function*() {
const parentSpan = yield* Tracer.ParentSpan
// Both layers will be children of "application-startup" span
const AppLayer = Layer.mergeAll(DatabaseLayer, CacheLayer).pipe(
Layer.withParentSpan(parentSpan)
)
const context = yield* Layer.build(AppLayer)
const database = Context.get(context, Database)
const cache = Context.get(context, Cache)
const dbResult = yield* database.query("SELECT * FROM users")
const cacheResult = yield* cache.get("user:123")
return { dbResult, cacheResult }
})
)
Effect.runSync(Effect.scoped(program)) // => { dbResult: "DB: SELECT * FROM users", cacheResult: "Cache: user:123" }

withSpan

Added in v2.0.0 Source

Wraps a Layer with a new tracing span, making all operations in the layer constructor part of the named trace span.

Details

This creates a new span for the layer's construction and execution. The span is automatically ended when the layer's scope is closed. This is useful for tracking the lifecycle and performance of layer initialization.

Signature

declare const withSpan: {
(name: string, options?: SpanOptions): <A, E, R>(self: Layer<A, E, R>) => Layer<A, E, Exclude<R, ParentSpan>>;
<A, E, R>(self: Layer<A, E, R>, name: string, options?: SpanOptions): Layer<A, E, Exclude<R, ParentSpan>>;
}

Example

(Wrapping a layer with a span)

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 logs: Array<string> = []
// Create layers with tracing
const databaseLayer = Layer.effect(Database, Effect.gen(function*() {
return {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
}
})).pipe(Layer.withSpan("database-initialization", {
attributes: { dbType: "postgres" }
}))
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(msg)))
}).pipe(Layer.withSpan("logger-initialization"))
// Combine traced layers
const appLayer = Layer.mergeAll(databaseLayer, loggerLayer).pipe(
Layer.withSpan("app-initialization", {
onEnd: (span, exit) =>
Effect.sync(() => logs.push(`Application initialization completed: ${exit._tag}`))
})
)
const program = Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
yield* logger.log("Application ready")
return yield* database.query("SELECT * FROM users")
}).pipe(Effect.provide(appLayer))
Effect.runSync(program) // => "Result: SELECT * FROM users"
logs // => ["Application ready", "Application initialization completed: Success"]

Utility Types

Any interface

Added in v3.9.0 Source

A type-level constraint for working with any Layer type.

When to use

Use to constrain generic parameters or layer collections to any Layer value while preserving its provided, error, and required service types for inference.

Details

This interface is used to constrain generic types to Layer values without specifying exact type parameters.

See

  • Layer for the concrete layer interface
  • Services for extracting required services from a layer type
  • Error for extracting construction errors from a layer type
  • Success for extracting provided services from a layer type

Signature

interface Any {
readonly "~effect/Layer": {
readonly _E: any;
readonly _RIn: any;
readonly _ROut: any;
};
}

Error type

Added in v2.0.0 Source

Extracts the error type (E) from a Layer type.

When to use

Use to derive a layer construction error type for helper types, wrappers, or APIs that preserve a layer failure channel.

See

  • Success for extracting the services provided by the same Layer
  • Services for extracting the dependency requirements of the same Layer

Signature

type Error<T extends Any> = T extends Layer<infer _ROut, infer _E, infer _RIn> ? _E : never

Ensures that a layer's error type extends a given type E.

Details

This function provides compile-time type checking to ensure that the error type of a layer conforms to a specific type constraint.

Signature

declare function satisfiesErrorType<E>(): <ROut, E2, RIn>(layer: Layer<ROut, E2, RIn>) => Layer<ROut, E2, RIn>

Example

(Constraining layer error types)

import { Effect, Layer } from "effect"
const typeErrorLayer = Layer.effectDiscard(Effect.fail(new TypeError("boom")))
// Define a constraint that the error type must be an Error
const satisfiesError = Layer.satisfiesErrorType<Error>()
// This works - Layer<never, TypeError, never> extends Layer<never, Error, never>
const validLayer = satisfiesError(typeErrorLayer)

Ensures that a layer's requirements type extends a given type R.

Details

This function provides compile-time type checking to ensure that the requirements type of a layer conforms to a specific type constraint.

Signature

declare function satisfiesServicesType<RIn>(): <ROut, E, RIn2>(layer: Layer<ROut, E, RIn2>) => Layer<ROut, E, RIn2>

Example

(Constraining layer service requirements)

import { Context, Effect, Layer } from "effect"
const NumberService = Context.Service<number>("Number")
const numberLayer = Layer.effectDiscard(Effect.asVoid(NumberService))
// Define a constraint that the service requirements must be numbers
const satisfiesNumber = Layer.satisfiesServicesType<number>()
// This works - Layer<never, never, 42> extends Layer<never, never, number>
const validLayer = satisfiesNumber(numberLayer)

Ensures that a layer's success type extends a given type ROut.

Details

This function provides compile-time type checking to ensure that the success value of a layer conforms to a specific type constraint.

Signature

declare function satisfiesSuccessType<ROut>(): <ROut2, E, RIn>(layer: Layer<ROut2, E, RIn>) => Layer<ROut2, E, RIn>

Example

(Constraining layer success types)

import { Context, Layer } from "effect"
const NumberService = Context.Service<number>("Number")
const numberLayer = Layer.succeed(NumberService, 42)
// Define a constraint that the success type must be a number
const satisfiesNumber = Layer.satisfiesSuccessType<number>()
// This works - Layer<42, never, never> extends Layer<number, never, never>
const validLayer = satisfiesNumber(numberLayer)

Services type

Added in v4.0.0 Source

Extracts the service requirements (RIn) from a Layer type.

When to use

Use to derive the dependency requirements of a generic or inferred Layer without restating its RIn type parameter.

See

  • Success for extracting the services provided by the same Layer
  • Error for extracting the construction failure type from the same Layer

Signature

type Services<T extends Any> = T extends infer L ? L extends Layer<infer _ROut, infer _E, infer _RIn> ? _RIn : never : never

Success type

Added in v2.0.0 Source

Extracts the service output type (ROut) from a Layer type.

When to use

Use to derive the services provided by an existing or generic Layer without restating its ROut type parameter.

See

  • Error for extracting the layer construction error type instead
  • Services for extracting the layer input service requirements instead

Signature

type Success<T extends Any> = T extends Layer<infer _ROut, infer _E, infer _RIn> ? _ROut : never

Zipping

merge

Added in v2.0.0 Source

Merges this layer with another layer concurrently, producing a new layer with combined input, error, and output types.

When to use

Use to combine an existing Layer with another Layer or an array of layers while preserving pipeline style.

Details

This is a binary version of mergeAll that merges exactly two layers or one layer with an array of layers. The layers are built concurrently and their outputs are combined.

See

  • mergeAll for merging several layers at once

Signature

declare const merge: {
<RIn, E, ROut>(that: Layer<ROut, E, RIn>): <RIn2, E2, ROut2>(self: Layer<ROut2, E2, RIn2>) => Layer<ROut | ROut2, E | E2, RIn | RIn2>;
<Layers extends [Any, ...Array<Any>]>(that: Layers): <A, E, R>(self: Layer<A, E, R>) => Layer<A | Success<Layers[number]>, E | Error<Layers[number]>, R | Services<Layers[number]>>;
<RIn2, E2, ROut2, RIn, E, ROut>(self: Layer<ROut2, E2, RIn2>, that: Layer<ROut, E, RIn>): Layer<ROut2 | ROut, E2 | E, RIn2 | RIn>;
<A, E, R, Layers extends [Any, ...Array<Any>]>(self: Layer<A, E, R>, that: Layers): Layer<A | Success<Layers[number]>, E | Error<Layers[number]>, R | Services<Layers[number]>>;
}

Example

(Merging two layers)

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: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((_msg: string) => Effect.void)
})
const mergedLayer = Layer.merge(dbLayer, loggerLayer)
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, mergedLayer)) // => "result"

mergeAll

Added in v2.0.0 Source

Combines all the provided layers concurrently, creating a new layer with merged input, error, and output types.

When to use

Use when you need to combine multiple independent layers.

Details

All layers are built concurrently, and their outputs are merged into a single layer.

If multiple merged layers depend on the same layer value, that dependency is shared by default. Reuse a named layer value when you want services to share the same resource, such as one database pool.

See

  • merge for merging one layer with another layer or array

Signature

declare function mergeAll<Layers extends [Layer<never, any, any>, ...Array<Layer<never, any, any>>]>(...layers: Layers): Layer<Success<Layers[number]>, Error<Layers[number]>, Services<Layers[number]>>

Example

(Merging independent layers)

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: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(msg)))
})
const mergedLayer = Layer.mergeAll(dbLayer, loggerLayer)
const program = Logger.use((logger) => logger.log("ready"))
Effect.runSync(Effect.provide(program, mergedLayer))
logs // => ["ready"]