Skip to content

ConfigProvider

Data sources used by Config to load raw configuration values. A ConfigProvider reads paths from places such as environment variables, JavaScript objects, .env contents, or directories, and returns a uniform Node shape that config schemas can decode. The module also includes helpers for composing providers, changing paths, and installing providers through layers.

20 exports Added in v2.0.0 Source

Combinators

constantCase

Added in v2.0.0 Source

Converts all string path segments to CONSTANT_CASE before lookup.

When to use

Use to bridge camelCase schema keys to SCREAMING_SNAKE_CASE environment variables.

Details

Numeric segments are left unchanged. String segments use String.configCase so numeric word groups such as v2 are preserved for environment variable names. This is a specialization of mapInput.

See

  • mapInput – for arbitrary path transformations

Signature

declare const constantCase: (self: ConfigProvider) => ConfigProvider;

mapInput

Added in v4.0.0 Source

Transforms the path segments before they reach the underlying store.

When to use

Use when you need to rename, re-case, or otherwise transform config path segments before lookup.

Details

The function f receives the whole path produced by earlier provider transformations and must return a new path. Lookup path transformations compose in application order: the existing transformation runs first, then f runs. For providers composed with orElse, the transformation is applied to each operand.

The combinator delegates transformation to the provider itself. Use make for custom sources so this capability is implemented automatically.

See

  • constantCase – a preset that converts to CONSTANT_CASE
  • nested – for prepending a prefix instead of transforming

Signature

declare const mapInput: {
  (f: (path: Path) => Path): (self: ConfigProvider) => ConfigProvider;
  (self: ConfigProvider, f: (path: Path) => Path): ConfigProvider;
};

nested

Added in v2.0.0 Source

Scopes a provider so that all lookups are prefixed with the given path segments.

When to use

Use to namespace config under a prefix like "app" or "database", or reuse the same provider shape for multiple sub-configs.

Details

Accepts a single string or a full Path array. For providers composed with orElse, the prefix is applied to each operand. Supports both data-last and data-first calling conventions.

Gotchas

Ordering matters when composing with mapInput or constantCase. Later provider transformations run after earlier ones: a later nested becomes the outer prefix, and a later mapInput sees the whole path produced by previous transformations.

See

  • mapInput – for arbitrary path transformations

Signature

declare const nested: {
  (prefix: string | Path): (self: ConfigProvider) => ConfigProvider;
  (self: ConfigProvider, prefix: string | Path): ConfigProvider;
};

orElse

Added in v2.0.0 Source

Returns a provider that falls back to that when self returns undefined for a path.

When to use

Use to layer multiple config sources, such as env vars plus a defaults file, or provide partial overrides on top of a base config.

Details

Each provider keeps its own path transformations. If the combined provider is later transformed with mapInput or nested, the transformation is applied to both sides.

Gotchas

The fallback only runs when the path is not found (undefined). A SourceError from self is not caught; it propagates immediately.

See

  • layerAdd – install a fallback provider via a Layer

Signature

declare const orElse: {
  (that: ConfigProvider): (self: ConfigProvider) => ConfigProvider;
  (self: ConfigProvider, that: ConfigProvider): ConfigProvider;
};

Constructors

fromDir

Added in v4.0.0 Source

Creates a ConfigProvider that reads configuration from a directory tree on disk, where each file is a leaf value and each directory is a container.

When to use

Use when you expose each config key as a file under a directory, such as Kubernetes ConfigMap or Secret volume mounts.

Details

Resolution tries a regular file first and returns a Value node for non-empty trimmed file contents. If the file read fails, it tries a directory and returns a Record node with immediate child names as keys. If both fail with NotFound, it returns undefined. Other platform failures return SourceError.

Requires Path and FileSystem in the Effect context. Defaults to root path /; override with { rootPath: "/etc/config" }.

Literal empty strings are treated as missing values by default after file contents are trimmed. Pass { preserveEmptyStrings: true } to keep empty strings as explicit values. Directory listings still reflect the file names present on disk.

See

Signature

declare const fromDir: (options?: {
  readonly preserveEmptyStrings?: boolean;
  readonly rootPath?: string;
}) => Effect.Effect<ConfigProvider, never, Path_.Path | FileSystem.FileSystem>;

fromDotEnv

Added in v4.0.0 Source

Creates a ConfigProvider by reading and parsing a .env file from the file system.

When to use

Use to load environment config from a .env file at application startup.

Details

Requires FileSystem in the Effect context. Defaults to reading ".env" in the current directory; override with { path: "/custom/.env" }. Variable expansion (for example, ${VAR}) is disabled by default; enable with { expandVariables: true }.

Literal empty strings are treated as missing values when loaded as values by default. Pass { preserveEmptyStrings: true } to keep empty strings as explicit values. Child discovery still reflects the keys present in the parsed .env source.

Returns an Effect that resolves to a ConfigProvider. Fails with a PlatformError if the file cannot be read.

See

Signature

declare const fromDotEnv: (options?: {
  readonly expandVariables?: boolean;
  readonly path?: string;
  readonly preserveEmptyStrings?: boolean;
}) => Effect.Effect<ConfigProvider, PlatformError, FileSystem.FileSystem>;

Creates a ConfigProvider by parsing the string contents of a .env file.

When to use

Use when you already have the .env contents as a string, such as contents fetched from a remote store or embedded in a test.

Details

Supports export prefixes, single/double/backtick quoting, inline comments, and escaped newlines. Variable expansion (for example, ${VAR}) is disabled by default; enable with { expandVariables: true }.

Literal empty strings are treated as missing values when loaded as values by default. Pass { preserveEmptyStrings: true } to keep empty strings as explicit values. Child discovery still reflects the keys present in the parsed .env source.

Parsing is based on the dotenv / dotenv-expand algorithm.

Internally delegates to fromEnv with the parsed key-value pairs.

See

  • fromDotEnv – loads a .env file from disk
  • fromEnv – for raw environment variable access

Signature

declare function fromDotEnvContents(
  lines: string,
  options?: {
    readonly expandVariables?: boolean;
    readonly preserveEmptyStrings?: boolean;
  },
): ConfigProvider;

fromEnv

Added in v2.0.0 Source

Creates a ConfigProvider backed by environment variables.

When to use

Use to read configuration from process.env, which is the default when no provider is explicitly set, or pass a custom env record for testing or non-Node runtimes.

Details

Path segments are joined with _ for direct lookup, and env var names are also split on _ to build a trie for child key discovery. This means DATABASE_HOST=localhost is accessible at both path ["DATABASE_HOST"] and ["DATABASE", "HOST"]. If all immediate children of a trie node have purely numeric names, the node is reported as an Array; otherwise as a Record.

The default environment merges process.env and import.meta.env (when available). Override by passing { env: { ... } }.

Literal empty strings are treated as missing values when loaded as values by default. Pass { preserveEmptyStrings: true } to keep empty strings as explicit values. Child discovery still reflects the environment variable names present in the source.

Never fails with SourceError — all lookups are synchronous.

See

Signature

declare function fromEnv(options?: {
  readonly env?: Record<string, string>;
  readonly preserveEmptyStrings?: boolean;
}): ConfigProvider;

fromUnknown

Added in v4.0.0 Source

Creates a ConfigProvider backed by an in-memory JavaScript value (typically a parsed JSON object).

When to use

Use when you need deterministic config from an in-memory JavaScript value, such as in tests, embedded config, or parsed JSON.

Details

Path traversal follows standard JS rules: string segments index into object keys, numeric segments index into arrays. Returns undefined for any path that cannot be resolved. Never fails with SourceError.

Primitive values (number, boolean, bigint) are stringified via String(...).

Literal empty strings are treated as missing values when loaded as values by default. Pass { preserveEmptyStrings: true } to keep empty strings as explicit values.

Gotchas

Object keys and array lengths reflect the original input shape. A leaf value of "" is treated as missing when that leaf is loaded, but the parent container still reports its original keys or length.

See

  • fromEnv – for environment variables
  • make – for custom backing stores

Signature

declare function fromUnknown(
  root: unknown,
  options?: {
    readonly preserveEmptyStrings?: boolean;
  },
): ConfigProvider;

make

Added in v4.0.0 Source

Creates a ConfigProvider from a raw lookup function.

When to use

Use when implementing a provider backed by a custom store, such as a database, remote API, or in-memory map.

Details

The get callback receives a Path and must return Effect<Node | undefined, SourceError>. Return undefined when the path does not exist, a Node when it does, and fail with SourceError only when the source cannot be read.

Providers created by make also implement the path-transformation capability used by mapInput, constantCase, and nested.

See

  • fromEnv – pre-built provider for environment variables
  • fromUnknown – pre-built provider for JSON objects

Signature

declare function make(get: (path: Path) => Effect<Node | undefined, SourceError>): ConfigProvider;

makeArray

Added in v4.0.0 Source

Creates an Array node representing an indexed container with a known length.

When to use

Use when you need to describe a JSON array or numerically indexed env vars inside a custom provider.

Details

The optional value allows a node to be both a container and a leaf at the same time.

See

Signature

declare function makeArray(length: number, value?: string): Node;

makeRecord

Added in v4.0.0 Source

Creates a Record node representing an object-like container with known child keys.

When to use

Use when you need to describe a directory or JSON object inside a custom provider.

Details

The optional value allows a node to be both a container and a leaf at the same time (for example, an env var A=x that also has children A_FOO and A_BAR).

See

Signature

declare function makeRecord(keys: ReadonlySet<string>, value?: string): Node;

makeValue

Added in v4.0.0 Source

Creates a Value node representing a terminal string leaf.

When to use

Use when building nodes inside a custom ConfigProvider's get callback.

Details

The function returns a new plain object.

See

Signature

declare function makeValue(value: string): Node;

Errors

SourceError

Added in v4.0.0 Source

Typed error indicating that a configuration source could not be read.

When to use

Use when you need to report that a custom provider's underlying store is unreachable or produced an I/O error while reading configuration data.

Gotchas

Do not use SourceError for "key not found". That case is represented by returning undefined from load.

See

Signature

declare class SourceError extends YieldableError<this> & {
  readonly _tag: "SourceError";
} & Readonly<{
  readonly cause?: unknown;
  readonly message: string;
}> {
  constructor(args: {
    readonly cause?: unknown;
    readonly message: string;
  });
}

Layers

layer

Added in v4.0.0 Source

Provides a layer that installs a ConfigProvider as the active provider for all downstream effects, replacing any previously installed provider.

When to use

Use to set the config source for an entire application or test suite.

Details

Accepts either a plain ConfigProvider or an Effect that produces one. When given an Effect, it is evaluated once when the layer is built.

See

  • layerAdd – add a provider without replacing the existing one

Signature

declare function layer<E = never, R = never>(
  self: ConfigProvider | Effect<ConfigProvider, E, R>,
): Layer<never, E, Exclude<R, Scope>>;

layerAdd

Added in v4.0.0 Source

Creates a Layer that composes a new ConfigProvider with the currently active one, rather than replacing it.

When to use

Use to add defaults that should only apply when the primary provider has no value for a path, or override specific keys while keeping the rest from the existing provider by setting asPrimary: true.

Details

By default, the new provider acts as a fallback and is consulted only when the current provider returns undefined. Set asPrimary: true to make the new provider the primary source, with the existing one as fallback.

See

  • layer – replace the provider entirely
  • orElse – compose providers without layers

Signature

declare function layerAdd<E = never, R = never>(
  self: ConfigProvider | Effect<ConfigProvider, E, R>,
  options?: {
    readonly asPrimary?: boolean;
  },
): Layer<never, E, Exclude<R, Scope>>;

Models

Node type

Added in v4.0.0 Source

A discriminated union describing the shape of a configuration value at a given path.

When to use

Use when implementing a custom ConfigProvider by returning raw nodes from the get callback passed to make, or when inspecting raw provider output before schema parsing.

Details

Value is a terminal string leaf. Record is an object-like container whose immediate child keys are known and may carry an optional co-located value. Array is an indexed container with a known length and may also carry an optional co-located value.

Provider lookups return undefined when no node exists at the requested path. Within a node that was found, value: undefined has a narrower structural meaning: the container exists but has no co-located scalar value.

See

Signature

type Node =
  | {
      readonly _tag: "Value";
      readonly value: string;
    }
  | {
      readonly _tag: "Record";
      readonly keys: ReadonlySet<string>;
      readonly value: string | undefined;
    }
  | {
      readonly _tag: "Array";
      readonly length: number;
      readonly value: string | undefined;
    };

Path type

Added in v4.0.0 Source

An ordered sequence of string or numeric segments that addresses a node in the configuration tree. String segments name object keys; numeric segments index into arrays.

When to use

Use to address raw configuration nodes when implementing or transforming a ConfigProvider.

Signature

type Path = ReadonlyArray<string | number>;

Services

Context reference for the active raw configuration provider, registered in the context with a default value of fromEnv(). Because it is a Context.Reference, it is available without explicit provision; Config schemas automatically resolve it.

When to use

Use to override the active raw configuration provider for an entire program, or retrieve the current provider inside an Effect.

See

  • layer – install a provider as a Layer
  • layerAdd – add a fallback provider as a Layer

Signature

declare const ConfigProvider: Reference<ConfigProvider>;

ConfigProvider interface

Added in v4.0.0 Source

The core interface for loading raw configuration data.

When to use

Use to type-annotate variables that hold a provider or to implement a custom provider via make.

Details

load(path) is the semantic lookup operation used by the Config module. It applies provider transformations and composition before consulting the underlying source. undefined means "not found", a Node means the path exists, and SourceError means the source itself failed.

mapInput(f) is the provider's path-transformation capability. Keeping this capability on the provider allows source and composite providers to preserve their own lookup behavior without exposing an internal representation. Transformations compose in application order: f receives the path produced by earlier transformations.

load deliberately accepts only a Path. Path transformation is modeled by returning another provider through mapInput, rather than by adding a transformation callback to every lookup. Custom implementations therefore expose lookup and transformation behavior, but no source or composition state.

See

  • make – construct a provider from a lookup function
  • orElse – compose providers with fallback

Signature

interface ConfigProvider extends Pipeable {
  readonly load: (path: Path) => Effect<Node | undefined, SourceError>;
  readonly mapInput: (f: (path: Path) => Path) => ConfigProvider;
}