Skip to content

Context.ts

Stores Effect services in typed maps.

A Context holds service implementations under Context.Service or Context.Reference keys, and its type records which keys are present. Effects use contexts as their environment, so services can be provided once instead of passed through every function call. This module includes helpers for creating keys, building contexts, adding and reading services, merging contexts, and selecting or removing services.

Since v4.0.0



Adds a service to a given Context.

When to use

Use when you need to store a known service value in a Context.

Details

If the context already contains the same service key, the new service replaces the previous one.

Example (Adding a service to a context)

import { Context, pipe } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const someContext = Context.make(Port, { PORT: 8080 })
const context = pipe(someContext, Context.add(Timeout, { TIMEOUT: 5000 }))
assert.deepStrictEqual(Context.get(context, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(context, Timeout), { TIMEOUT: 5000 })

See

  • addOrOmit for adding or removing a service from an Option

Signature

declare const add: {
<I, S>(key: Key<I, S>, service: Types.NoInfer<S>): <Services>(self: Context<Services>) => Context<Services | I>
<Services, I, S>(self: Context<Services>, key: Key<I, S>, service: Types.NoInfer<S>): Context<Services | I>
}

Source

Since v2.0.0

Adds or removes a service depending on an Option.

When to use

Use when you need to add or omit a Context service based on an Option.

Details

When service is Option.some, the value is stored for the key. When it is Option.none, the key is removed from the returned Context.

Example (Adding optional services)

import { Context, Option } from "effect"
const Port = Context.Service<{ PORT: number }>("Port")
const withPort = Context.empty().pipe(Context.addOrOmit(Port, Option.some({ PORT: 8080 })))
const withoutPort = withPort.pipe(Context.addOrOmit(Port, Option.none()))

See

  • add for always storing a service value

Signature

declare const addOrOmit: {
<I, S>(
key: Key<I, S>,
service: Option.Option<Types.NoInfer<S>>
): <Services>(self: Context<Services>) => Context<Services | I>
<Services, I, S>(
self: Context<Services>,
key: Key<I, S>,
service: Option.Option<Types.NoInfer<S>>
): Context<Services | I>
}

Source

Since v4.0.0

Merges two Contexts into one.

When to use

Use when you need to combine two contexts.

Details

When both contexts contain the same service key, the service from that overrides the service from self.

Example (Merging two contexts)

import { Context } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const firstContext = Context.make(Port, { PORT: 8080 })
const secondContext = Context.make(Timeout, { TIMEOUT: 5000 })
const context = Context.merge(firstContext, secondContext)
assert.deepStrictEqual(Context.get(context, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(context, Timeout), { TIMEOUT: 5000 })

See

  • mergeAll for merging more than two contexts at once

Signature

declare const merge: {
<R1>(that: Context<R1>): <Services>(self: Context<Services>) => Context<R1 | Services>
<Services, R1>(self: Context<Services>, that: Context<R1>): Context<Services | R1>
}

Source

Since v2.0.0

Merges any number of Contexts into one.

When to use

Use when you need to combine a variadic list of contexts.

Details

When multiple contexts contain the same service key, the service from the last context with that key is kept.

Example (Merging multiple contexts)

import { Context } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const Host = Context.Service<{ HOST: string }>("Host")
const firstContext = Context.make(Port, { PORT: 8080 })
const secondContext = Context.make(Timeout, { TIMEOUT: 5000 })
const thirdContext = Context.make(Host, { HOST: "localhost" })
const context = Context.mergeAll(firstContext, secondContext, thirdContext)
assert.deepStrictEqual(Context.get(context, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(context, Timeout), { TIMEOUT: 5000 })
assert.deepStrictEqual(Context.get(context, Host), { HOST: "localhost" })

See

  • merge for merging two contexts

Signature

declare const mergeAll: <T extends Array<unknown>>(...ctxs: { [K in keyof T]: Context<T[K]> }) => Context<T[number]>

Source

Since v3.12.0

Creates a Context service key.

When to use

Use when you need to define a context service key for a dependency that must be provided by the surrounding context.

Details

Call Context.Service("Key") for a function-style key, or use the two-stage form Context.Service<Self, Shape>()("Key") for class-style service declarations. The returned key can be yielded as an Effect and passed to Context.make, Context.add, and the Context getter functions.

Gotchas

The string key is the runtime identity of the service. Reusing the same key string for unrelated services makes them occupy the same slot in a Context.

Example (Creating service keys)

import { Context } from "effect"
// Create a simple service
const Database = Context.Service<{
query: (sql: string) => string
}>("Database")
// Create a service class
class Config extends Context.Service<
Config,
{
port: number
}
>()("Config") {}
// Use the services to create contexts
const db = Context.make(Database, {
query: (sql) => `Result: ${sql}`
})
const config = Context.make(Config, { port: 8080 })

See

  • Reference for service keys with default values

Signature

declare const Service: {
<Identifier, Shape = Identifier>(key: string): Service<Identifier, Shape>
<Self, Shape>(): <const Identifier extends string, E, R = Types.unassigned, Args extends ReadonlyArray<any> = never>(
id: Identifier,
options?: { readonly make: ((...args: Args) => Effect<Shape, E, R>) | Effect<Shape, E, R> | undefined } | undefined
) => ServiceClass<Self, Identifier, Shape> &
([Types.unassigned] extends [R]
? unknown
: { readonly make: [Args] extends [never] ? Effect<Shape, E, R> : (...args: Args) => Effect<Shape, E, R> })
<Self>(): <
const Identifier extends string,
Make extends Effect<any, any, any> | ((...args: any) => Effect<any, any, any>)
>(
id: Identifier,
options: { readonly make: Make }
) => ServiceClass<
Self,
Identifier,
Make extends Effect<infer _A, infer _E, infer _R> | ((...args: infer _Args) => Effect<infer _A, infer _E, infer _R>)
? _A
: never
> & { readonly make: Make }
}

Source

Since v4.0.0

Returns an empty Context.

Example (Creating an empty context)

import { Context } from "effect"
import * as assert from "node:assert"
assert.strictEqual(Context.isContext(Context.empty()), true)

Signature

declare const empty: () => Context<never>

Source

Since v2.0.0

Creates a new Context with a single service associated to the key.

Example (Creating a context with one service)

import { Context } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const context = Context.make(Port, { PORT: 8080 })
assert.deepStrictEqual(Context.get(context, Port), { PORT: 8080 })

Signature

declare const make: <I, S>(key: Key<I, S>, service: Types.NoInfer<S>) => Context<I>

Source

Since v2.0.0

Creates a Context from an existing service map.

When to use

Use when constructing a low-level Context from a trusted map whose lifecycle you control.

Gotchas

This is unsafe because later mutation of the provided map can affect the created Context. Prefer empty, make, add, or merge for normal Context construction.

Example (Creating a context from a map)

import { Context } from "effect"
// Create a context from a Map (unsafe)
const map = new Map([["Logger", { log: (msg: string) => console.log(msg) }]])
const context = Context.makeUnsafe(map)

Signature

declare const makeUnsafe: <Services = never>(mapUnsafe: ReadonlyMap<string, any>) => Context<Services>

Source

Since v4.0.0

Returns a new Context with the specified service keys removed.

When to use

Use when you want to remove a denylist of services from a Context.

Example (Omitting services from a context)

import { Context, Option, pipe } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const someContext = pipe(Context.make(Port, { PORT: 8080 }), Context.add(Timeout, { TIMEOUT: 5000 }))
const context = pipe(someContext, Context.omit(Timeout))
assert.deepStrictEqual(Context.getOption(context, Port), Option.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(context, Timeout), Option.none())

See

  • pick for keeping selected services

Signature

declare const omit: <S extends ReadonlyArray<Key<any, any>>>(
...keys: S
) => <Services>(self: Context<Services>) => Context<Exclude<Services, Service.Identifier<S[number]>>>

Source

Since v2.0.0

Returns a new Context that contains only the specified services.

When to use

Use when you want to keep an allowlist of services in a Context.

Example (Picking services from a context)

import { Context, Option, pipe } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const someContext = pipe(Context.make(Port, { PORT: 8080 }), Context.add(Timeout, { TIMEOUT: 5000 }))
const context = pipe(someContext, Context.pick(Port))
assert.deepStrictEqual(Context.getOption(context, Port), Option.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(context, Timeout), Option.none())

See

  • omit for removing selected services

Signature

declare const pick: <S extends ReadonlyArray<Key<any, any>>>(
...services: S
) => <Services>(self: Context<Services>) => Context<Services & Service.Identifier<S[number]>>

Source

Since v2.0.0

Gets a service from the context that corresponds to the given key.

When to use

Use when you need type-checked access to a service already included in the context type.

Example (Getting a service from a context)

import { Context, pipe } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const context = pipe(Context.make(Port, { PORT: 8080 }), Context.add(Timeout, { TIMEOUT: 5000 }))
assert.deepStrictEqual(Context.get(context, Timeout), { TIMEOUT: 5000 })

See

  • getOption for optional service access
  • getOrElse for fallback values

Signature

declare const get: {
<Services, I extends Services, S>(service: Key<I, S>): (self: Context<Services>) => S
<Services, I extends Services, S>(self: Context<Services>, service: Key<I, S>): S
}

Source

Since v2.0.0

Gets the service for a key safely wrapped in an Option.

When to use

Use when you need to read a Context service as an Option so absence is represented as data.

Details

Returns Option.some when the service is stored in the context. If the key is a Context.Reference and no override is stored, returns Option.some of the cached default value. Missing non-reference keys return Option.none.

Example (Getting optional services)

import { Context, Option } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const context = Context.make(Port, { PORT: 8080 })
assert.deepStrictEqual(Context.getOption(context, Port), Option.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(context, Timeout), Option.none())

See

  • getOrElse for returning a fallback value directly

Signature

declare const getOption: {
<S, I>(service: Key<I, S>): <Services>(self: Context<Services>) => Option.Option<S>
<Services, S, I>(self: Context<Services>, service: Key<I, S>): Option.Option<S>
}

Source

Since v2.0.0

Gets the service for a key, or evaluates the fallback when a non-reference key is absent.

When to use

Use when you need a fallback for a missing Context.Service key while still resolving Context.Reference defaults.

Details

If the key is a Context.Reference and no override is stored in the context, its cached default value is returned instead of the fallback.

Gotchas

The fallback is not evaluated for missing Context.Reference keys because references resolve to their default value.

Example (Falling back for missing services)

import { Context } from "effect"
const Logger = Context.Service<{ log: (msg: string) => void }>("Logger")
const Database = Context.Service<{ query: (sql: string) => string }>("Database")
const context = Context.make(Logger, {
log: (msg: string) => console.log(msg)
})
const logger = Context.getOrElse(context, Logger, () => ({ log: () => {} }))
const database = Context.getOrElse(context, Database, () => ({ query: () => "fallback" }))
console.log(logger === Context.get(context, Logger)) // true
console.log(database.query("SELECT 1")) // "fallback"

See

  • getOption for returning Option.none when a non-reference key is missing

Signature

declare const getOrElse: {
<S, I, B>(key: Key<I, S>, orElse: LazyArg<B>): <Services>(self: Context<Services>) => S | B
<Services, S, I, B>(self: Context<Services>, key: Key<I, S>, orElse: LazyArg<B>): S | B
}

Source

Since v3.7.0

Returns the service currently stored for a key, or undefined when the key is absent.

When to use

Use when you need to read the service stored for a key without resolving Context.Reference defaults.

Gotchas

This is a raw lookup and does not resolve default values for Context.Reference keys.

See

  • getOption for a reference-aware optional lookup

Signature

declare const getOrUndefined: {
<S, I>(key: Key<I, S>): <Services>(self: Context<Services>) => S | undefined
<Services, S, I>(self: Context<Services>, key: Key<I, S>): S | undefined
}

Source

Since v4.0.0

Checks whether the provided argument is a Context.

When to use

Use to narrow an unknown value before passing it to APIs that require a Context.

Details

This checks the runtime Context marker and does not inspect which services the context contains.

Gotchas

This guard only proves that the value is a Context; it does not prove that any specific service is present.

Example (Checking for contexts)

import { Context } from "effect"
import * as assert from "node:assert"
assert.strictEqual(Context.isContext(Context.empty()), true)

See

  • isKey for checking service keys
  • isReference for checking references with defaults

Signature

declare const isContext: (u: unknown) => u is Context<never>

Source

Since v2.0.0

Checks whether the provided argument is a Key.

Example (Checking for keys)

import { Context } from "effect"
import * as assert from "node:assert"
assert.strictEqual(Context.isKey(Context.Service("Service")), true)

Signature

declare const isKey: (u: unknown) => u is Key<any, any>

Source

Since v4.0.0

Checks whether the provided argument is a Reference.

Example (Checking for references)

import { Context } from "effect"
import * as assert from "node:assert"
const LoggerRef = Context.Reference("Logger", {
defaultValue: () => ({ log: (msg: string) => console.log(msg) })
})
assert.strictEqual(Context.isReference(LoggerRef), true)
assert.strictEqual(Context.isReference(Context.Service("Key")), false)

Signature

declare const isReference: (u: unknown) => u is Reference<any>

Source

Since v3.11.0

Immutable collection of service implementations used for dependency injection in Effect programs.

Details

The type parameter tracks the service identifiers available in the context. At runtime, services are stored by each key’s string key.

Example (Creating a context with multiple services)

import { Context } from "effect"
// Create a context with multiple services
const Logger = Context.Service<{ log: (msg: string) => void }>("Logger")
const Database = Context.Service<{ query: (sql: string) => string }>("Database")
const context = Context.make(Logger, {
log: (msg: string) => console.log(msg)
}).pipe(Context.add(Database, { query: (sql) => `Result: ${sql}` }))

Signature

export interface Context<in Services> extends Equal.Equal, Pipeable, Inspectable {
readonly [TypeId]: {
readonly _Services: Types.Contravariant<Services>
}
readonly mapUnsafe: ReadonlyMap<string, any>
mutable: boolean
}

Source

Since v2.0.0

Typed identifier for a service stored in a Context.

When to use

Use as the typed handle for storing, retrieving, and requiring a specific service in a Context.

Details

Identifier tracks the requirement in Effect types, while Shape is the service implementation retrieved by the key. A key is also an Effect value, so yielding it inside Effect.gen retrieves the service from the current fiber context.

See

  • Service for creating required service keys
  • Reference for creating service keys with default values

Signature

export interface Key<out Identifier, out Shape> extends Effect<Shape, never, Identifier> {
readonly [ServiceTypeId]: ServiceTypeId
readonly Service: Shape
readonly Identifier: Identifier
readonly key: string
readonly stack?: string | undefined
}

Source

Since v4.0.0

Service key with a lazily computed default value.

Details

When a Reference is requested from a Context that does not contain an override, Context getters that resolve references return the cached default value instead of failing.

Example (Defining a reference with a default value)

import { Context } from "effect"
// Define a reference with a default value
const LoggerRef: Context.Reference<{ log: (msg: string) => void }> = Context.Reference("Logger", {
defaultValue: () => ({ log: (msg: string) => console.log(msg) })
})
// The reference can be used without explicit provision
const context = Context.empty()
const logger = Context.get(context, LoggerRef) // Uses default value

Signature

export interface Reference<in out Shape> extends Service<never, Shape> {
readonly [ReferenceTypeId]: typeof ReferenceTypeId
readonly defaultValue: () => Shape
[Symbol.iterator](): EffectIterator<Reference<Shape>>
new (_: never): {}
}

Source

Since v3.11.0

Context key with helper methods for working with a service.

Details

context creates a one-service Context, use and useSync retrieve the service from the current Effect context before applying a function, and of is a type-level helper for service values.

Example (Defining a service key)

import { Context } from "effect"
// Define an identifier for a database service
const Database = Context.Service<{ query: (sql: string) => string }>("Database")
// The key can be used to store and retrieve services
const context = Context.make(Database, { query: (sql) => `Result: ${sql}` })

Signature

export interface Service<in out Identifier, in out Shape> extends Key<Identifier, Shape> {
of(this: void, self: Shape): Shape
context(self: Shape): Context<Identifier>
use<A, E, R>(f: (service: Shape) => Effect<A, E, R>): Effect<A, E, R | Identifier>
useSync<A>(f: (service: Shape) => A): Effect<A, never, Identifier>
}

Source

Since v4.0.0

Class-style service key produced by Context.Service<Self, Shape>()("Id").

When to use

Use when declaring a service as a class so the class value can serve as the Context key.

Details

The class itself is the Context key, and its string key identifies the service at runtime.

See

  • Service for creating function-style keys or class-style service keys

Signature

export interface ServiceClass<in out Self, in out Identifier extends string, in out Shape> extends Service<
Self,
Shape
> {
new (_: never): ServiceClass.Shape<Identifier, Shape>
readonly key: Identifier
}

Source

Since v4.0.0

Performs a series of mutations on a Context. Prevents unnecessary copying of the underlying map when multiple mutations are needed.

When to use

Use to apply several Context transformations in one callback while copying the underlying service map only once.

See

  • add for adding or replacing a service
  • addOrOmit for adding or removing a service from an Option
  • merge for combining two contexts
  • pick for keeping selected services
  • omit for removing selected services

Signature

declare const mutate: {
<Services, B>(f: (context: Context<Services>) => Context<B>): <Services>(self: Context<Services>) => Context<B>
<Services, B>(self: Context<Services>, f: (context: Context<Services>) => Context<B>): Context<B>
}

Source

Since v4.0.0

Creates a context key with a default value.

When to use

Use when you need to define a context key with a lazily computed default value.

Details

Context.Reference allows you to create a key that can hold a value. You can provide a default value for the service, which will automatically be used when the context is accessed, or override it with a custom implementation when needed. The default value is computed lazily and cached on the reference.

Example (Creating references with default values)

import { Context } from "effect"
// Create a reference with a default value
const LoggerRef = Context.Reference("Logger", {
defaultValue: () => ({ log: (msg: string) => console.log(msg) })
})
// The reference provides the default value when accessed from an empty context
const context = Context.empty()
const logger = Context.get(context, LoggerRef)
// You can also override the default value
const customContext = Context.make(LoggerRef, {
log: (msg: string) => `Custom: ${msg}`
})
const customLogger = Context.get(customContext, LoggerRef)

See

  • Service for required services without default values

Signature

declare const Reference: <Service>(key: string, options: { readonly defaultValue: () => Service }) => Reference<Service>

Source

Since v3.11.0

Runtime type identifier attached to Context service keys and used by isKey to recognize them.

Signature

declare const ServiceTypeId: "~effect/Context/Service"

Source

Since v4.0.0

String literal type used as the runtime type identifier for Context service keys.

Signature

type ServiceTypeId = "~effect/Context/Service"

Source

Since v4.0.0

Gets the value for a Context.Reference, returning its cached default when the context does not contain an override.

When to use

Use when you need a Context.Reference value resolved from either a stored override or the reference’s default value.

Details

Stored overrides take precedence. If no override is present, the reference’s default value is computed lazily and cached on the reference itself.

Gotchas

Mutable default values can be shared across contexts unless an override is provided, because the default is cached on the Context.Reference.

Example (Getting reference defaults unsafely)

import { Context } from "effect"
const LoggerRef = Context.Reference("Logger", {
defaultValue: () => ({ log: (msg: string) => console.log(msg) })
})
const context = Context.empty()
const logger = Context.getReferenceUnsafe(context, LoggerRef)
console.log(typeof logger.log) // "function"

See

  • getUnsafe for unsafe access with any service key
  • get for type-checked reference-aware access
  • getOption for optional access to non-reference keys

Signature

declare const getReferenceUnsafe: <Services, S>(self: Context<Services>, service: Reference<S>) => S

Source

Since v4.0.0

Gets the service for a key, throwing if an absent non-reference key cannot be resolved.

When to use

Use when you need to read a service from a context whose type does not prove the service is present.

Details

If the key is a Context.Reference and no override is stored in the context, its cached default value is returned. For absent non-reference keys, this function throws a runtime error.

Example (Getting services unsafely)

import { Context } from "effect"
import * as assert from "node:assert"
const Port = Context.Service<{ PORT: number }>("Port")
const Timeout = Context.Service<{ TIMEOUT: number }>("Timeout")
const context = Context.make(Port, { PORT: 8080 })
assert.deepStrictEqual(Context.getUnsafe(context, Port), { PORT: 8080 })
assert.throws(() => Context.getUnsafe(context, Timeout))

See

  • get for type-checked service access
  • getOption for optional service access

Signature

declare const getUnsafe: {
<S, I>(service: Key<I, S>): <Services>(self: Context<Services>) => S
<Services, S, I>(self: Context<Services>, services: Key<I, S>): S
}

Source

Since v4.0.0

Namespace containing utility types for Context service keys.

Example (Extracting service types)

import { Context } from "effect"
const Database = Context.Service<{
query: (sql: string) => string
}>("Database")
// Extract service type from a key
type DatabaseService = Context.Service.Shape<typeof Database>
// Extract identifier type from a key
type DatabaseId = Context.Service.Identifier<typeof Database>

Source

Since v2.0.0

Type that matches any Context service key regardless of its identifier or service shape.

Example (Typing any service key)

import { Context } from "effect"
// Any represents any possible service type
const services: Array<Context.Service.Any> = [
Context.Service<{ log: (msg: string) => void }>("Logger"),
Context.Service<{ query: (sql: string) => string }>("Database")
]

Signature

type Any = Key<never, any> | Key<any, any>

Source

Since v4.0.0

Extracts the service implementation type stored behind a Context service key.

Example (Extracting a service shape)

import { Context } from "effect"
const Database = Context.Service<{ query: (sql: string) => string }>("Database")
// Extract the service shape from the service
type DatabaseService = Context.Service.Shape<typeof Database>
// DatabaseService is { query: (sql: string) => string }

Signature

type Shape<T> = T extends Key<infer _I, infer S> ? S : never

Source

Since v4.0.0

Extracts the identifier, or requirement type, associated with a Context service key.

Example (Extracting a service identifier)

import { Context } from "effect"
const Database = Context.Service<{ query: (sql: string) => string }>("Database")
// Extract the identifier type from a key
type DatabaseId = Context.Service.Identifier<typeof Database>
// DatabaseId is the identifier type

Signature

type Identifier<T> = T extends Key<infer I, infer _S> ? I : never

Source

Since v2.0.0

Namespace containing helper types for class-style Context.Service declarations.

Source

Since v4.0.0

Runtime and type-level metadata carried by a class-style service key, including its service type identifier, string key, and service shape.

Signature

export interface Shape<Identifier extends string, Service> {
readonly [ServiceTypeId]: typeof ServiceTypeId
readonly key: Identifier
readonly Service: Service
}

Source

Since v4.0.0