Config
Descriptions of configuration values that can be read from a
ConfigProvider. A Config<T> explains which keys to read, how to decode
and validate them, and how to combine defaults, fallbacks, nested paths, and
multiple settings. Configs are also Effects, so they can be yielded in
Effect.gen after a provider has been supplied.
Combinators
Combines multiple configs into a single config that parses all of them.
When to use
Use when you need to group related configs into a tuple or named struct.
Details
Accepts a tuple (preserves positions), an iterable, or a record of configs. Returns a config whose parsed value mirrors the input shape.
A combined config is absent when at least one child cannot resolve and none of the other children read provider input. This lets withDefault and option handle a wholly absent group. Once any child reads input, a missing sibling makes the group incomplete and parsing fails. Values supplied by child defaults do not count as provider input.
Unlike a Schema.Struct passed to schema, all only considers input
read by its children. An explicitly present but empty parent container does
not by itself make the group present.
Signature
declare function all<Arg extends Iterable<Config<any>, any, any> | Record<string, Config<any>>>(arg: Arg): Config<[Arg] extends [readonly Array<Config<any>>] ? { [K in string | number | symbol]: [Arg[K]] extends [Config<A>] ? A : never } : [Arg] extends [Iterable<Config<A>, any, any>] ? Array<A> : [Arg] extends [Record<string, Config<any>>] ? { [K in string | number | symbol]: [Arg[K]] extends [Config<A>] ? A : never } : never>Example
(Combining configs as a struct)
import { Config, ConfigProvider, Effect } from "effect"
const dbConfig = Config.all({ host: Config.string("host"), port: Config.number("port")})
const provider = ConfigProvider.fromUnknown({ host: "localhost", port: 5432 })Effect.runSync(dbConfig.parse(provider)) // => { host: "localhost", port: 5432 }Scopes a config under a named prefix.
When to use
Use when you need to group related config keys under a common namespace.
Details
The prefix is prepended to every key the inner config reads. With
fromUnknown this means an extra object level; with fromEnv it means
a _-separated prefix on env var names.
Multiple nested calls compose: the outermost name becomes the
outermost path segment.
See
Signature
declare const nested: { (name: string): <A>(self: Config<A>) => Config<A>; <A>(self: Config<A>, name: string): Config<A>;}Example
import { Config, ConfigProvider, Effect } from "effect"
const dbConfig = Config.all({ host: Config.string("host"), port: Config.number("port")}).pipe(Config.nested("database"))
const provider = ConfigProvider.fromUnknown({ database: { host: "localhost", port: "5432" }})Effect.runSync(dbConfig.parse(provider)) // => { host: "localhost", port: 5432 }Example
(Reading env vars with a nested prefix)
import { Config, ConfigProvider, Effect } from "effect"
const host = Config.string("host").pipe(Config.nested("database"))
const provider = ConfigProvider.fromEnv({ env: { database_host: "localhost" }})Effect.runSync(host.parse(provider)) // => "localhost"Makes a config optional: returns Some(value) on success and None when the
config cannot resolve because none of its relevant input is present.
When to use
Use when you need to handle a config key that may or may not be present.
Gotchas
Validation errors and partially supplied groups still propagate. Successful
values are always wrapped in Some, including undefined when the schema
explicitly accepts it. Schema configs first represent a missing or
incompatible provider shape as undefined; None is returned only when the
schema rejects that value and no relevant input was found.
See
- withDefault – provide a concrete fallback value instead
Signature
declare function option<A>(self: Config<A>): Config<Option<A>>Example
(Reading optional config)
import { Config, ConfigProvider, Effect, Option } from "effect"
const maybePort = Config.option(Config.number("port"))
const provider = ConfigProvider.fromUnknown({})Effect.runSync(maybePort.parse(provider)) // => Option.none()Provides a fallback config when parsing fails with a ConfigError.
When to use
Use when you need to try an alternative config source after the primary one fails.
Details
Unlike withDefault, this handles both semantic absence and all
ConfigErrors. The fallback function receives the error and returns a new
Config.
Gotchas
Recovery preserves whether the primary config read provider input. When the recovered config is composed with all, invalid input in the primary branch still makes the enclosing group partially supplied, so an outer withDefault or option does not replace the whole group.
See
- withDefault – fallback only on semantic absence
Signature
declare const orElse: { <A2>(that: (error: ConfigError) => Config<A2>): <A>(self: Config<A>) => Config<A2 | A>; <A, A2>(self: Config<A>, that: (error: ConfigError) => Config<A2>): Config<A | A2>;}Example
(Falling back to a literal)
import { Config, ConfigProvider, Effect } from "effect"
const hostConfig = Config.string("HOST").pipe( Config.orElse(() => Config.succeed("localhost")))const provider = ConfigProvider.fromUnknown({})Effect.runSync(hostConfig.parse(provider)) // => "localhost"withDefault
Provides a fallback value when the config cannot resolve because none of its relevant input is present.
When to use
Use when you need to make a config key optional with a sensible default.
Gotchas
Validation errors and partially supplied groups still propagate. A schema
that successfully decodes absent input also keeps its decoded value instead
of using the default. Schema configs first represent a missing or
incompatible provider shape as undefined; the default is used only when
the schema rejects that value and no relevant input was found.
See
Signature
declare const withDefault: { <A2>(defaultValue: A2): <A>(self: Config<A>) => Config<A2 | A>; <A, A2>(self: Config<A>, defaultValue: A2): Config<A | A2>;}Example
(Defaulting a missing port)
import { Config, ConfigProvider, Effect } from "effect"
const port = Config.number("port").pipe(Config.withDefault(3000))
const provider = ConfigProvider.fromUnknown({})Effect.runSync(port.parse(provider)) // => 3000Constructors
Creates a config for a boolean value parsed from common string representations.
When to use
Use to read boolean flags from string-like config sources.
Details
Shortcut for Config.schema(Config.Boolean, name).
Accepted values: true, false, yes, no, on, off, 1, 0,
y, n.
See
- Boolean for the underlying boolean codec
Signature
declare function boolean(name?: string): Config<boolean>Example
(Reading a boolean flag)
import { Config, ConfigProvider, Effect } from "effect"
const program = Config.boolean("FEATURE_FLAG")
const provider = ConfigProvider.fromEnv({ env: { FEATURE_FLAG: "yes" }})
Effect.runSync( program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider))) // => trueCreates a config for a Date value parsed from a string.
When to use
Use to read date settings that must parse to valid Date values.
Details
Shortcut for Config.schema(Schema.Date, name).
Gotchas
Fails with a SchemaError if the string produces an invalid Date.
Signature
declare function date(name?: string): Config<Date>Example
(Reading a date)
import { Config, ConfigProvider, Effect } from "effect"
const createdAt = Config.date("CREATED_AT")
const provider = ConfigProvider.fromUnknown({ CREATED_AT: "2024-01-15" })Effect.runSync(createdAt.parse(provider)).toISOString() // => "2024-01-15T00:00:00.000Z"Creates a config for a Duration value parsed from a human-readable
string.
When to use
Use to read time duration settings such as timeouts, intervals, or TTLs.
Details
Shortcut for Config.schema(Schema.DurationFromString, name).
Accepts any string that Duration.fromInput can parse (e.g.
"10 seconds", "500 millis", "Infinity", "-Infinity").
See
- schema for decoding configuration values with a custom codec
Signature
declare function duration(name?: string): Config<Duration>Example
(Reading a duration)
import { Config, ConfigProvider, Duration, Effect } from "effect"
const program = Config.duration("DURATION").pipe(Effect.map(Duration.toMillis))
const provider = ConfigProvider.fromEnv({ env: { DURATION: "10 seconds" }})
Effect.runSync( program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider))) // => 10000Creates a config that always fails with the given error.
When to use
Use when you need to re-raise a specific config error, such as inside orElse.
Signature
declare function fail(err: SchemaError | SourceError): Config<unknown>Creates a config for a finite number (rejects NaN and Infinity).
When to use
Use to read a numeric config value that must be finite.
Details
Shortcut for Config.schema(Schema.Finite, name).
See
Signature
declare function finite(name?: string): Config<number>Creates a config for an integer value. Rejects floats.
When to use
Use to read a numeric config value that must be an integer.
Details
Shortcut for Config.schema(Schema.Int, name).
See
Signature
declare function int(name?: string): Config<number>Creates a config that only accepts a specific literal value.
When to use
Use to restrict a config to a single, specific literal value.
Details
Shortcut for Config.schema(Schema.Literal(literal), name).
See
- literals – accepts multiple literal values
Signature
declare function literal<L extends LiteralValue>(literal: L, name?: string): Config<L>Example
(Restricting to a literal)
import { Config, ConfigProvider, Effect } from "effect"
const env = Config.literal("production", "ENV")const provider = ConfigProvider.fromUnknown({ ENV: "production" })Effect.runSync(env.parse(provider)) // => "production"Creates a config that only accepts one of the specified literal values.
When to use
Use to restrict a config to a fixed set of allowed literal values.
Details
Shortcut for Config.schema(Schema.Literals(literals), name).
See
- literal for accepting one specific literal value
Signature
declare function literals<L extends readonly Array<LiteralValue>>(literals: L, name?: string): Config<L[number]>Example
(Restricting to a set of literals)
import { Config, ConfigProvider, Effect } from "effect"
const env = Config.literals(["development", "production"], "ENV")const provider = ConfigProvider.fromUnknown({ ENV: "development" })Effect.runSync(env.parse(provider)) // => "development"Creates a config for a log level string.
When to use
Use to read Effect log-level settings from configuration.
Details
Shortcut for Config.schema(Config.LogLevel, name).
Accepted values: "All", "Fatal", "Error", "Warn", "Info",
"Debug", "Trace", "None".
See
- LogLevel for the underlying log-level codec
Signature
declare function logLevel(name?: string): Config<LogLevel>Example
(Reading a log level)
import { Config, ConfigProvider, Effect } from "effect"
const program = Config.logLevel("LOG_LEVEL")
const provider = ConfigProvider.fromEnv({ env: { LOG_LEVEL: "Info" }})
Effect.runSync( program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider))) // => "Info"nonEmptyString
Creates a config for a non-empty string value. Fails if the value is an empty string.
When to use
Use to read a string config value that must contain at least one character.
Details
Shortcut for Config.schema(Schema.NonEmptyString, name).
See
- string for allowing empty strings
Signature
declare function nonEmptyString(name?: string): Config<string>Creates a config for a numeric value (including NaN, Infinity).
When to use
Use when you need config input to accept JavaScript's full number domain, including NaN and infinities, rather than reject non-finite values.
Details
Shortcut for Config.schema(Schema.Number, name).
See
Signature
declare function number(name?: string): Config<number>Creates a config for a port number (integer in 1–65535).
When to use
Use to read network port settings that must be valid port numbers.
Details
Shortcut for Config.schema(Config.Port, name).
See
Signature
declare function port(name?: string): Config<number>Example
(Reading a port)
import { Config, ConfigProvider, Effect } from "effect"
const program = Config.port("PORT")
const provider = ConfigProvider.fromEnv({ env: { PORT: "8080" }})
Effect.runSync( program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider))) // => 8080Creates a config for a redacted string value. The parsed result is wrapped
in a Redacted container that hides the value from logs and toString.
When to use
Use to read secret string settings that should not be exposed in logs or string output.
Details
Shortcut for Config.schema(Schema.Redacted(Schema.String), name).
See
- string for non-secret string settings
Signature
declare function redacted(name?: string): Config<Redacted<string>>Example
(Reading a secret)
import { Config, ConfigProvider, Effect } from "effect"
const program = Config.redacted("API_KEY").pipe(Effect.map(String))
const provider = ConfigProvider.fromEnv({ env: { API_KEY: "sk-1234567890abcdef" }})
Effect.runSync( program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider))) // => "<redacted>"Creates a config for a single string value.
When to use
Use when reading a single string env var or config key.
Details
Shortcut for Config.schema(Schema.String, name).
See
- nonEmptyString – rejects empty strings
- schema – for more complex types
Signature
declare function string(name?: string): Config<string>Example
(Reading a string config)
import { Config, ConfigProvider, Effect } from "effect"
const host = Config.string("HOST")
const provider = ConfigProvider.fromUnknown({ HOST: "localhost" })Effect.runSync(host.parse(provider)) // => "localhost"Creates a config that always succeeds with the given value, ignoring the provider entirely.
When to use
Use when you need a hardcoded config value, such as inside orElse or tests.
Signature
declare function succeed<T>(value: T): Config<T>Example
(Returning a constant fallback)
import { Config, ConfigProvider, Effect } from "effect"
const host = Config.string("HOST").pipe( Config.orElse(() => Config.succeed("localhost")))const provider = ConfigProvider.fromUnknown({})Effect.runSync(host.parse(provider)) // => "localhost"Creates a config for a URL value parsed from a string.
When to use
Use to read configuration values that must be valid URL strings.
Details
This is a shortcut for Config.schema(Schema.URL, name).
Gotchas
Fails if the string cannot be parsed by the URL constructor.
See
- schema for decoding configuration values with a custom codec
Signature
declare function url(name?: string): Config<URL>Example
(Reading a URL)
import { Config, ConfigProvider, Effect } from "effect"
const program = Config.url("URL").pipe(Effect.map((url) => url.href))
const provider = ConfigProvider.fromEnv({ env: { URL: "https://example.com" }})
Effect.runSync( program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider))) // => "https://example.com/"Converting
Constructs a Config<T> from a value matching Wrap<T>.
When to use
Use when accepting config from callers who may pass either a single Config or a
record of individual Configs.
Details
If the input is already a Config, it is returned as-is. Otherwise, each
key is recursively unwrapped and combined.
See
- Wrap – the utility type accepted by this function
Signature
declare function unwrap<T>(wrapped: Wrap<T>): Config<T>Example
(Unwrapping a record of configs)
import { Config, ConfigProvider, Effect } from "effect"
interface Options { key: string}
const makeConfig = (config: Config.Wrap<Options>): Config.Config<Options> => Config.unwrap(config)
const config = makeConfig({ key: Config.string("key") })const provider = ConfigProvider.fromUnknown({ key: "value" })Effect.runSync(config.parse(provider)) // => { key: "value" }Errors
ConfigError
Represents the error type produced when config loading or validation fails.
When to use
Use when you need to inspect config loading or validation failures.
Details
Wraps either:
- A
SourceError— the provider could not read data (I/O failure). - A
SchemaError— the data was found but did not match the schema (wrong type, out of range, missing key, etc.).
See
- orElse – recover from a ConfigError
- withDefault – provide a fallback when relevant input is absent
Signature
declare class ConfigError { constructor(cause: SchemaError | SourceError); readonly _tag: "ConfigError"; readonly cause: SchemaError | SourceError; readonly name: string; message: string; toString(): string;}Guards
Returns true if u is a Config instance.
When to use
Use when you need to distinguish a Config from an unknown value before
calling .parse or unwrap.
Signature
declare function isConfig(u: unknown): u is Config<unknown>Example
(Checking Config values)
import { Config } from "effect"
Config.isConfig(Config.string("HOST")) // => trueConfig.isConfig("not a config") // => falseMapping
Transforms the parsed value of a config with a pure function.
When to use
Use when you need to transform a parsed config value with a function that cannot fail.
See
- mapOrFail – when the transformation can fail
Signature
declare const map: { <A, B>(f: (a: A) => B): (self: Config<A>) => Config<B>; <A, B>(self: Config<A>, f: (a: A) => B): Config<B>;}Example
(Uppercasing a string config)
import { Config, ConfigProvider, Effect } from "effect"
const upper = Config.string("name").pipe( Config.map((s) => s.toUpperCase()))
const provider = ConfigProvider.fromUnknown({ name: "alice" })Effect.runSync(upper.parse(provider)) // => "ALICE"Transforms the parsed value with a function that may fail.
When to use
Use when you need to transform a parsed config value with a function that can
produce a ConfigError (e.g. parsing a URL, checking a range).
See
- map – when the transformation cannot fail
Signature
declare const mapOrFail: { <A, B>(f: (a: A) => Effect<B, ConfigError>): (self: Config<A>) => Config<B>; <A, B>(self: Config<A>, f: (a: A) => Effect<B, ConfigError>): Config<B>;}Example
(Wrapping a value in an effectful transformation)
import { Config, ConfigProvider, Effect } from "effect"
const trimmed = Config.string("name").pipe( Config.mapOrFail((s) => Effect.succeed(s.trim())))const provider = ConfigProvider.fromUnknown({ name: " Alice " })Effect.runSync(trimmed.parse(provider)) // => "Alice"Models
A recipe for extracting a typed value T from a ConfigProvider.
When to use
Use to describe typed configuration that can be parsed from a provider or
yielded inside Effect.gen.
Details
Key members:
parse(provider)– runs the config against a specific provider.- Yieldable – can be yielded inside
Effect.gen, which automatically resolves the currentConfigProviderfrom the context. - Pipeable – supports
.pipe(Config.map(...))etc.
See
- schema – the main way to create a Config
Signature
interface Config<out T> extends Effect<T, ConfigError> { readonly "~effect/Config": "~effect/Config"; readonly parse: (provider: ConfigProvider) => Effect<T, ConfigError>;}Other
Schemas
Schema for boolean values encoded as strings.
When to use
Use when you need the reusable boolean schema value for Config.schema with
custom paths.
Details
Accepted string values: true, false, yes, no, on, off, 1,
0, y, n (case-sensitive).
See
- boolean – convenience constructor
Signature
declare const Boolean: decodeTo<Boolean, Literals<readonly ["true", "yes", "on", "1", "y", "false", "no", "off", "0", "n"]>, never, never>Schema for LogLevel string literals.
When to use
Use when you need the reusable log-level schema value for Config.schema
with custom paths.
Details
Accepted values: "All", "Fatal", "Error", "Warn", "Info",
"Debug", "Trace", "None".
See
- logLevel – convenience constructor
Signature
declare const LogLevel: Literals<readonly Array<LogLevel>>Schema for port numbers (integers in 1–65535).
When to use
Use when you need the reusable port schema value for Config.schema with
custom paths.
See
- port – convenience constructor
Signature
declare const Port: IntSchema for key-value record types that can also be parsed from a flat comma-separated string.
When to use
Use when reading key-value maps from a single env var (e.g. OpenTelemetry resource attributes).
Details
Accepts either a JSON-like record from the provider or a flat string like
"key1=val1,key2=val2". The separator (default ",") and
keyValueSeparator (default "=") can be customized.
See
- Array for separated or structural array input
Signature
declare function Record<K extends Key, V extends Constraint>(key: K, value: V, options?: { readonly keyValueSeparator?: string; readonly separator?: string;}): Union<readonly [$Record<K, V>, decodeTo<toCodecStringTree<$Record<K, V>>, String, never, never>]>Example
(Parsing a comma-separated record)
import { Config, ConfigProvider, Effect, Schema } from "effect"
const schema = Config.Record(Schema.String, Schema.String)const config = Config.schema(schema, "OTEL_RESOURCE_ATTRIBUTES")
const provider = ConfigProvider.fromEnv({ env: { OTEL_RESOURCE_ATTRIBUTES: "service.name=my-service,service.version=1.0.0,custom.attribute=value" }})
const result = Effect.runSync(config.parse(provider))result["service.name"] // => "my-service"result["service.version"] // => "1.0.0"result["custom.attribute"] // => "value"Creates a Config<T> from a Schema.Codec.
When to use
Use when you need to read structured or schema-validated configuration.
Details
The optional path sets the local path segment(s) for the config lookup.
It is appended to the logical path prefix accumulated from outer
nested calls. Pass a single string for a flat key or an array for
nested paths.
Convenience constructors such as string, number, and boolean delegate
to this API.
The codec is converted to its canonical StringTree form. Its encoded shape
determines how provider data is loaded: scalar schemas read a co-located
scalar value, object schemas read declared properties and matching record
keys, and array schemas read indexed children. A mixed-shape union loads each
member according to that member's shape before applying the union's mode and
checks.
At the config's lookup path, a missing node or a node that cannot provide the
representation required by the schema is decoded as undefined. Missing
object properties remain omitted so the schema's property semantics still
apply. Decoding success always wins, even when no provider input was found.
For example,
Schema.UndefinedOr(Schema.String) decodes to undefined and is not replaced
by withDefault. If decoding fails and no relevant representation was
found, the config is absent. Invalid data in a relevant representation is a
validation failure. Provider SourceErrors are always failures.
Gotchas
Plain Schema.Array and Schema.Record schemas use structural provider
input. Use Array or Record when a flat separated string must
also be accepted.
Schema.Struct and all describe different lookup models. An
explicitly present empty object is relevant input for a struct and required
fields are validated. The same empty parent container does not make an
all group present when all of its child configs are absent.
The canonical StringTree encoding must expose a concrete scalar, object,
array, or union shape. Opaque encodings such as Schema.Any,
Schema.Unknown, Schema.ObjectKeyword, Schema.Json, and
Schema.MutableJson are rejected synchronously when this config is
constructed, including when they are nested in another schema. Suspended
recursive schemas remain supported when their eventual shape is concrete.
Declarations such as Schema.URL also remain supported when their canonical
encoding has a concrete shape. To read arbitrary JSON from one scalar value,
use Schema.fromJsonString(Schema.Json).
See
Signature
declare function schema<T>(codec: ConstraintCodec<T, unknown>, path?: string | Path): Config<T>Example
(Reading a structured config)
import { Config, ConfigProvider, Effect, Schema } from "effect"
const DbConfig = Config.schema( Schema.Struct({ host: Schema.String, port: Schema.Int }), "db")
const provider = ConfigProvider.fromUnknown({ db: { host: "localhost", port: 5432 }})
Effect.runSync(DbConfig.parse(provider)) // => { host: "localhost", port: 5432 }Utility Types
Extracts the successfully parsed value type from a Config.
When to use
Use to derive the parsed value type from an existing Config value when
declaring reusable config-driven types.
See
- Config for the config type whose parsed value is extracted
- Effect.Success for extracting the success type from any
Effect
Signature
type Success<T> = [T] extends [Config<infer A>] ? A : neverUtility type that recursively replaces primitives with Config in a nested
structure.
When to use
Use when typing the input of unwrap so callers can pass either a Config
or a record of Configs.
Details
Config.Wrap<{ key: string }> becomes { key: Config<string> } | Config<{ key: string }>
See
- unwrap – construct a
Configfrom aWrap<T>
Signature
type Wrap<A> = [NonNullable<A>] extends [infer T] ? [IsPlainObject<T>] extends [true] ? { [K in keyof A]: Wrap<A[K]> } | Config<A> : Config<A> : Config<A>