Skip to content

Configuration

Configuration is an essential aspect of any cloud-native application. Effect simplifies the process of managing configuration by offering a convenient interface for configuration providers.

The configuration front-end in Effect enables ecosystem libraries and applications to specify their configuration requirements in a declarative manner. It offloads the complex tasks to a ConfigProvider, which can be supplied by third-party libraries.

Effect comes bundled with a straightforward default ConfigProvider that retrieves configuration data from environment variables. This default provider can be used during development or as a starting point before transitioning to more advanced configuration providers.

To make our application configurable, we need to understand three essential elements:

  • Config Description: We describe the configuration data using an instance of Config<A>. If the configuration data is simple, such as a string, number, or boolean, we can use the built-in functions provided by the Config module. For more complex data types like HostPort, we can combine primitive configs to create a custom configuration description.

  • Config Frontend: We utilize the instance of Config<A> to load the configuration data described by the instance (a Config is, in itself, an effect). This process leverages the current ConfigProvider to retrieve the configuration.

  • Config Backend: The ConfigProvider serves as the underlying engine that manages the configuration loading process. Effect comes with a default config provider as part of its default services. This default provider reads the configuration data from environment variables. If we want to use a custom config provider, we can utilize the Layer.setConfigProvider layer to configure the Effect runtime accordingly.

Effect provides a set of primitives for the most common types like string, number, boolean, integer, etc.

Let’s start with a simple example of how to read configuration from environment variables:

1
import {
import Effect
Effect
,
import Config
Config
} from "effect"
2
3
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<string, ConfigError, never>> | YieldWrap<Effect.Effect<number, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
4
const
const host: string
host
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("HOST")
5
const
const port: number
port
= yield*
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

number
("PORT")
6
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(`Application started: ${
const host: string
host
}:${
const port: number
port
}`)
7
})
8
9
import Effect
Effect
.
const runSync: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>) => void
runSync
(
const program: Effect.Effect<void, ConfigError, never>
program
)

If we run this program we will get the following output:

Terminal window
npx tsx primitives.ts
(Missing data at HOST: "Expected HOST to exist in the process context")

This is because we have not provided any configuration. Let’s try running it with the following environment variables:

Terminal window
HOST=localhost PORT=8080 npx tsx primitives.ts
Application started: localhost:8080

Effect offers these basic types out of the box:

TypeDescription
stringConstructs a config for a string value.
numberConstructs a config for a float value.
booleanConstructs a config for a boolean value.
integerConstructs a config for an integer value.
dateConstructs a config for a date value.
literalConstructs a config for a literal (*) value.
logLevelConstructs a config for a LogLevel value.
durationConstructs a config for a duration value.
redactedConstructs a config for a secret value.

(*) string | number | boolean | null | bigint

In some cases, you may encounter situations where an environment variable is not set, leading to a missing value in the configuration. To handle such scenarios, Effect provides the Config.withDefault function. This function allows you to specify a fallback or default value to use when an environment variable is not present.

Here’s how you can use Config.withDefault to handle fallback values:

1
import {
import Effect
Effect
,
import Config
Config
} from "effect"
2
3
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<string, ConfigError, never>> | YieldWrap<Effect.Effect<number, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
4
const
const host: string
host
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("HOST")
5
const
const port: number
port
= yield*
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

number
("PORT").
(method) Pipeable.pipe<Config.Config<number>, Config.Config<number>>(this: Config.Config<number>, ab: (_: Config.Config<number>) => Config.Config<number>): Config.Config<...> (+21 overloads)
pipe
(
import Config
Config
.
const withDefault: <8080>(def: 8080) => <A>(self: Config.Config<A>) => Config.Config<8080 | A> (+1 overload)

Returns a config that describes the same structure as this one, but has the specified default value in case the information cannot be found.

withDefault
(8080))
6
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(`Application started: ${
const host: string
host
}:${
const port: number
port
}`)
7
})
8
9
import Effect
Effect
.
const runSync: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>) => void
runSync
(
const program: Effect.Effect<void, ConfigError, never>
program
)

When running the program with the command:

Terminal window
HOST=localhost npx tsx withDefault.ts

you will see the following output:

Terminal window
Application started: localhost:8080

Even though the PORT environment variable is not set, the fallback value of 8080 is used, ensuring that the program continues to run smoothly with a default value.

Effect provides several built-in constructors. These are functions that take a Config as input and produce another Config.

  • array: Constructs a config for an array of values.
  • chunk: Constructs a config for a sequence of values.
  • option: Returns an optional version of this config, which will be None if the data is missing from configuration, and Some otherwise.
  • repeat: Returns a config that describes a sequence of values, each of which has the structure of this config.
  • hashSet: Constructs a config for a sequence of values.
  • hashMap: Constructs a config for a sequence of values.

In addition to the basic ones, there are three special constructors you might find useful:

  • succeed: Constructs a config which contains the specified value.
  • fail: Constructs a config that fails with the specified message.
  • all: Constructs a config from a tuple / struct / arguments of configs.

Example

1
import {
import Effect
Effect
,
import Config
Config
} from "effect"
2
3
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<string[], ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<string[], ConfigError, never>>, void, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
4
const
const config: string[]
config
= yield*
import Config
Config
.
const array: <string>(config: Config.Config<string>, name?: string) => Config.Config<string[]>

Constructs a config for an array of values.

array
(
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
(), "MY_ARRAY")
5
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(
const config: string[]
config
)
6
})
7
8
import Effect
Effect
.
const runSync: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>) => void
runSync
(
const program: Effect.Effect<void, ConfigError, never>
program
)
Terminal window
MY_ARRAY=a,b,c npx tsx array.ts
[ 'a', 'b', 'c' ]

Effect comes with a set of built-in operators to help you manipulate and handle configurations.

These operators allow you to transform a config into a new one:

  • validate: Returns a config that describes the same structure as this one, but which performs validation during loading.
  • map: Creates a new config with the same structure as the original but with values transformed using a given function.
  • mapAttempt: Similar to map, but if the function throws an error, it’s caught and turned into a validation error.
  • mapOrFail: Like map, but allows for functions that might fail. If the function fails, it results in a validation error.

Example

1
import {
import Effect
Effect
,
import Config
Config
} from "effect"
2
3
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<string, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<string, ConfigError, never>>, void, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
4
const
const config: string
config
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("NAME").
(method) Pipeable.pipe<Config.Config<string>, Config.Config<string>>(this: Config.Config<string>, ab: (_: Config.Config<string>) => Config.Config<string>): Config.Config<...> (+21 overloads)
pipe
(
5
import Config
Config
.
const validate: <string>(options: { readonly message: string; readonly validation: Predicate<string>; }) => (self: Config.Config<string>) => Config.Config<string> (+3 overloads)

Returns a config that describes the same structure as this one, but which performs validation during loading.

validate
({
6
(property) message: string
message
: "Expected a string at least 4 characters long",
7
(property) validation: Predicate<string>
validation
: (
(parameter) s: string
s
) =>
(parameter) s: string
s
.
(property) String.length: number

Returns the length of a String object.

length
>= 4
8
})
9
)
10
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(
const config: string
config
)
11
})
12
13
import Effect
Effect
.
const runSync: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>) => void
runSync
(
const program: Effect.Effect<void, ConfigError, never>
program
)
Terminal window
NAME=foo npx tsx validate.ts
[(Invalid data at NAME: "Expected a string at least 4 characters long")]

These operators help you set up fallbacks in case of errors or missing data:

  • orElse: Sets up a config that tries to use this config first. If there’s an issue, it falls back to another specified config.
  • orElseIf: This one also tries to use the main config first but switches to a fallback config if there’s an error that matches a specific condition.

Example

In this example, we have a program that requires two configurations: A and B. We will use two configuration providers, where each provider has only one of the configurations. We will demonstrate how to set up fallbacks using the orElse operator.

1
import {
import Config
Config
,
import ConfigProvider
ConfigProvider
,
import Effect
Effect
,
import Layer
Layer
} from "effect"
2
3
// A program that requires two configurations: A and B
4
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<string, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<string, ConfigError, never>>, void, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
5
const
const A: string
A
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("A")
6
const
const B: string
B
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("B")
7
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(`A: ${
const A: string
A
}`, `B: ${
const B: string
B
}`)
8
})
9
10
const
const provider1: ConfigProvider.ConfigProvider
provider1
=
import ConfigProvider
ConfigProvider
.
const fromMap: (map: Map<string, string>, config?: Partial<ConfigProvider.ConfigProvider.FromMapConfig>) => ConfigProvider.ConfigProvider

Constructs a ConfigProvider using a map and the specified delimiter string, which determines how to split the keys in the map into path segments.

fromMap
(
11
new
var Map: MapConstructor new <string, string>(iterable?: Iterable<readonly [string, string]> | null | undefined) => Map<string, string> (+3 overloads)
Map
([
12
["A", "A"]
13
// B is missing
14
])
15
)
16
17
const
const provider2: ConfigProvider.ConfigProvider
provider2
=
import ConfigProvider
ConfigProvider
.
const fromMap: (map: Map<string, string>, config?: Partial<ConfigProvider.ConfigProvider.FromMapConfig>) => ConfigProvider.ConfigProvider

Constructs a ConfigProvider using a map and the specified delimiter string, which determines how to split the keys in the map into path segments.

fromMap
(
18
new
var Map: MapConstructor new <string, string>(iterable?: Iterable<readonly [string, string]> | null | undefined) => Map<string, string> (+3 overloads)
Map
([
19
// A is missing
20
["B", "B"]
21
])
22
)
23
24
const
const layer: Layer.Layer<never, never, never>
layer
=
import Layer
Layer
.
const setConfigProvider: (configProvider: ConfigProvider.ConfigProvider) => Layer.Layer<never>

Sets the current `ConfigProvider`.

setConfigProvider
(
25
const provider1: ConfigProvider.ConfigProvider
provider1
.
(method) Pipeable.pipe<ConfigProvider.ConfigProvider, ConfigProvider.ConfigProvider>(this: ConfigProvider.ConfigProvider, ab: (_: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider): ConfigProvider.ConfigProvider (+21 overloads)
pipe
(
import ConfigProvider
ConfigProvider
.
const orElse: (that: LazyArg<ConfigProvider.ConfigProvider>) => (self: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider (+1 overload)

Returns a new config provider that preferentially loads configuration data from this one, but which will fall back to the specified alternate provider if there are any issues loading the configuration from this provider.

orElse
(() =>
const provider2: ConfigProvider.ConfigProvider
provider2
))
26
)
27
28
import Effect
Effect
.
const runSync: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>) => void
runSync
(
import Effect
Effect
.
const provide: <void, ConfigError, never, never, never, never>(self: Effect.Effect<void, ConfigError, never>, layer: Layer.Layer<never, never, never>) => Effect.Effect<...> (+9 overloads)

Splits the context into two parts, providing one part using the specified layer/context/runtime and leaving the remainder `R0`

provide
(
const program: Effect.Effect<void, ConfigError, never>
program
,
const layer: Layer.Layer<never, never, never>
layer
))
Terminal window
npx tsx orElse.ts
A: A B: B

In addition to primitive types, we can also define configurations for custom types. To achieve this, we use primitive configs and combine them using Config operators (zip, orElse, map, etc.) and constructors (array, hashSet, etc.).

Let’s consider the HostPort data type, which consists of two fields: host and port.

1
class
class HostPort
HostPort
{
2
constructor(readonly
(property) HostPort.host: string
host
: string, readonly
(property) HostPort.port: number
port
: number) {}
3
}

We can define a configuration for this data type by combining primitive configs for string and number:

1
import {
import Config
Config
} from "effect"
2
3
class
class HostPort
HostPort
{
4
constructor(readonly
(property) HostPort.host: string
host
: string, readonly
(property) HostPort.port: number
port
: number) {}
5
6
get
(getter) HostPort.url: string
url
() {
7
return `${this.
(property) HostPort.host: string
host
}:${this.
(property) HostPort.port: number
port
}`
8
}
9
}
10
11
const
const both: Config.Config<[string, number]>
both
=
import Config
Config
.
const all: <readonly [Config.Config<string>, Config.Config<number>]>(arg: readonly [Config.Config<string>, Config.Config<number>]) => Config.Config<[string, number]>

Constructs a config from a tuple / struct / arguments of configs.

all
([
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("HOST"),
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

number
("PORT")])
12
13
const
const config: Config.Config<HostPort>
config
=
import Config
Config
.
const map: <[string, number], HostPort>(self: Config.Config<[string, number]>, f: (a: [string, number]) => HostPort) => Config.Config<HostPort> (+1 overload)

Returns a config whose structure is the same as this one, but which produces a different value, constructed using the specified function.

map
(
14
const both: Config.Config<[string, number]>
both
,
15
([
(parameter) host: string
host
,
(parameter) port: number
port
]) => new
constructor HostPort(host: string, port: number): HostPort
HostPort
(
(parameter) host: string
host
,
(parameter) port: number
port
)
16
)

In the above example, we use the Config.all(configs) operator to combine two primitive configs Config<string> and Config<number> into a Config<[string, number]>.

If we use this customized configuration in our application:

1
import {
import Effect
Effect
,
import Config
Config
} from "effect"
2
3
class
class HostPort
HostPort
{
4
constructor(readonly
(property) HostPort.host: string
host
: string, readonly
(property) HostPort.port: number
port
: number) {}
5
6
get
(getter) HostPort.url: string
url
() {
7
return `${this.
(property) HostPort.host: string
host
}:${this.
(property) HostPort.port: number
port
}`
8
}
9
}
10
11
const
const both: Config.Config<[string, number]>
both
=
import Config
Config
.
const all: <readonly [Config.Config<string>, Config.Config<number>]>(arg: readonly [Config.Config<string>, Config.Config<number>]) => Config.Config<[string, number]>

Constructs a config from a tuple / struct / arguments of configs.

all
([
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("HOST"),
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

number
("PORT")])
12
13
const
const config: Config.Config<HostPort>
config
=
import Config
Config
.
const map: <[string, number], HostPort>(self: Config.Config<[string, number]>, f: (a: [string, number]) => HostPort) => Config.Config<HostPort> (+1 overload)

Returns a config whose structure is the same as this one, but which produces a different value, constructed using the specified function.

map
(
14
const both: Config.Config<[string, number]>
both
,
15
([
(parameter) host: string
host
,
(parameter) port: number
port
]) => new
constructor HostPort(host: string, port: number): HostPort
HostPort
(
(parameter) host: string
host
,
(parameter) port: number
port
)
16
)
17
18
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<HostPort, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<HostPort, ConfigError, never>>, void, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
19
const
const hostPort: HostPort
hostPort
= yield*
const config: Config.Config<HostPort>
config
20
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(`Application started: ${
const hostPort: HostPort
hostPort
.
(property) HostPort.url: string
url
}`)
21
})

when you run the program using Effect.runSync(program), it will attempt to read the corresponding values from environment variables (HOST and PORT):

Terminal window
HOST=localhost PORT=8080 npx tsx App.ts
Application started: localhost:8080

So far, we have learned how to define configurations in a top-level manner, whether they are for primitive or custom types. However, we can also define nested configurations.

Let’s assume we have a ServiceConfig data type that consists of two fields: hostPort and timeout.

1
import {
import Config
Config
} from "effect"
2
3
class
class HostPort
HostPort
{
4
constructor(readonly
(property) HostPort.host: string
host
: string, readonly
(property) HostPort.port: number
port
: number) {}
5
6
get
(getter) HostPort.url: string
url
() {
7
return `${this.
(property) HostPort.host: string
host
}:${this.
(property) HostPort.port: number
port
}`
8
}
9
}
10
11
class
class ServiceConfig
ServiceConfig
{
12
constructor(readonly
(property) ServiceConfig.hostPort: HostPort
hostPort
:
class HostPort
HostPort
, readonly
(property) ServiceConfig.timeout: number
timeout
: number) {}
13
}

If we use this customized config in our application, it tries to read corresponding values from environment variables: HOST, PORT, and TIMEOUT.

However, in many cases, we don’t want to read all configurations from the top-level namespace. Instead, we may want to nest them under a common namespace. For example, we want to read both HOST and PORT from the HOSTPORT namespace, and TIMEOUT from the root namespace.

To achieve this, we can use the Config.nested combinator. It allows us to nest configs under a specific namespace. Here’s how we can update our configuration:

1
import {
import Config
Config
} from "effect"
2
3
class
class HostPort
HostPort
{
4
constructor(readonly
(property) HostPort.host: string
host
: string, readonly
(property) HostPort.port: number
port
: number) {}
5
6
get
(getter) HostPort.url: string
url
() {
7
return `${this.
(property) HostPort.host: string
host
}:${this.
(property) HostPort.port: number
port
}`
8
}
9
}
10
11
const
const both: Config.Config<[string, number]>
both
=
import Config
Config
.
const all: <readonly [Config.Config<string>, Config.Config<number>]>(arg: readonly [Config.Config<string>, Config.Config<number>]) => Config.Config<[string, number]>

Constructs a config from a tuple / struct / arguments of configs.

all
([
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("HOST"),
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

number
("PORT")])
12
13
const
const config: Config.Config<HostPort>
config
=
import Config
Config
.
const map: <[string, number], HostPort>(self: Config.Config<[string, number]>, f: (a: [string, number]) => HostPort) => Config.Config<HostPort> (+1 overload)

Returns a config whose structure is the same as this one, but which produces a different value, constructed using the specified function.

map
(
14
const both: Config.Config<[string, number]>
both
,
15
([
(parameter) host: string
host
,
(parameter) port: number
port
]) => new
constructor HostPort(host: string, port: number): HostPort
HostPort
(
(parameter) host: string
host
,
(parameter) port: number
port
)
16
)
17
18
class
class ServiceConfig
ServiceConfig
{
19
constructor(readonly
(property) ServiceConfig.hostPort: HostPort
hostPort
:
class HostPort
HostPort
, readonly
(property) ServiceConfig.timeout: number
timeout
: number) {}
20
}
21
22
const
const serfviceConfig: Config.Config<ServiceConfig>
serfviceConfig
=
import Config
Config
.
const map: <[HostPort, number], ServiceConfig>(self: Config.Config<[HostPort, number]>, f: (a: [HostPort, number]) => ServiceConfig) => Config.Config<...> (+1 overload)

Returns a config whose structure is the same as this one, but which produces a different value, constructed using the specified function.

map
(
23
import Config
Config
.
const all: <readonly [Config.Config<HostPort>, Config.Config<number>]>(arg: readonly [Config.Config<HostPort>, Config.Config<number>]) => Config.Config<[HostPort, number]>

Constructs a config from a tuple / struct / arguments of configs.

all
([
24
import Config
Config
.
const nested: <HostPort>(self: Config.Config<HostPort>, name: string) => Config.Config<HostPort> (+1 overload)

Returns a config that has this configuration nested as a property of the specified name.

nested
(
const config: Config.Config<HostPort>
config
, "HOSTPORT"),
25
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

number
("TIMEOUT")
26
]),
27
([
(parameter) hostPort: HostPort
hostPort
,
(parameter) timeout: number
timeout
]) => new
constructor ServiceConfig(hostPort: HostPort, timeout: number): ServiceConfig
ServiceConfig
(
(parameter) hostPort: HostPort
hostPort
,
(parameter) timeout: number
timeout
)
28
)

Now, if we run our application, it will attempt to read the corresponding values from the environment variables: HOSTPORT_HOST, HOSTPORT_PORT, and TIMEOUT.

When testing services, there are scenarios where we need to provide specific configurations to them. In such cases, we should be able to mock the backend that reads the configuration data.

To accomplish this, we can use the ConfigProvider.fromMap constructor. This constructor takes a Map<string, string> that represents the configuration data, and it returns a config provider that reads the configuration from that map.

Once we have the mock config provider, we can use Layer.setConfigProvider function. This function allows us to override the default config provider and provide our own custom config provider. It returns a Layer that can be used to configure the Effect runtime for our test specs.

Here’s an example of how we can mock a config provider for testing purposes:

1
import {
import Config
Config
,
import ConfigProvider
ConfigProvider
,
import Layer
Layer
,
import Effect
Effect
} from "effect"
2
3
class
class HostPort
HostPort
{
4
constructor(readonly
(property) HostPort.host: string
host
: string, readonly
(property) HostPort.port: number
port
: number) {}
5
6
get
(getter) HostPort.url: string
url
() {
7
return `${this.
(property) HostPort.host: string
host
}:${this.
(property) HostPort.port: number
port
}`
8
}
9
}
10
11
const
const both: Config.Config<[string, number]>
both
=
import Config
Config
.
const all: <readonly [Config.Config<string>, Config.Config<number>]>(arg: readonly [Config.Config<string>, Config.Config<number>]) => Config.Config<[string, number]>

Constructs a config from a tuple / struct / arguments of configs.

all
([
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

string
("HOST"),
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

number
("PORT")])
12
13
const
const config: Config.Config<HostPort>
config
=
import Config
Config
.
const map: <[string, number], HostPort>(self: Config.Config<[string, number]>, f: (a: [string, number]) => HostPort) => Config.Config<HostPort> (+1 overload)

Returns a config whose structure is the same as this one, but which produces a different value, constructed using the specified function.

map
(
14
const both: Config.Config<[string, number]>
both
,
15
([
(parameter) host: string
host
,
(parameter) port: number
port
]) => new
constructor HostPort(host: string, port: number): HostPort
HostPort
(
(parameter) host: string
host
,
(parameter) port: number
port
)
16
)
17
18
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<HostPort, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<HostPort, ConfigError, never>>, void, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
19
const
const hostPort: HostPort
hostPort
= yield*
const config: Config.Config<HostPort>
config
20
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(`Application started: ${
const hostPort: HostPort
hostPort
.
(property) HostPort.url: string
url
}`)
21
})
22
23
// Create a mock config provider using ConfigProvider.fromMap
24
const
const mockConfigProvider: ConfigProvider.ConfigProvider
mockConfigProvider
=
import ConfigProvider
ConfigProvider
.
const fromMap: (map: Map<string, string>, config?: Partial<ConfigProvider.ConfigProvider.FromMapConfig>) => ConfigProvider.ConfigProvider

Constructs a ConfigProvider using a map and the specified delimiter string, which determines how to split the keys in the map into path segments.

fromMap
(
25
new
var Map: MapConstructor new <string, string>(iterable?: Iterable<readonly [string, string]> | null | undefined) => Map<string, string> (+3 overloads)
Map
([
26
["HOST", "localhost"],
27
["PORT", "8080"]
28
])
29
)
30
31
// Create a layer using Layer.setConfigProvider to override the default config provider
32
const
const layer: Layer.Layer<never, never, never>
layer
=
import Layer
Layer
.
const setConfigProvider: (configProvider: ConfigProvider.ConfigProvider) => Layer.Layer<never>

Sets the current `ConfigProvider`.

setConfigProvider
(
const mockConfigProvider: ConfigProvider.ConfigProvider
mockConfigProvider
)
33
34
// Run the program using the provided layer
35
import Effect
Effect
.
const runSync: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>) => void
runSync
(
import Effect
Effect
.
const provide: <void, ConfigError, never, never, never, never>(self: Effect.Effect<void, ConfigError, never>, layer: Layer.Layer<never, never, never>) => Effect.Effect<...> (+9 overloads)

Splits the context into two parts, providing one part using the specified layer/context/runtime and leaving the remainder `R0`

provide
(
const program: Effect.Effect<void, ConfigError, never>
program
,
const layer: Layer.Layer<never, never, never>
layer
))
36
// Output: Application started: localhost:8080

By using this approach, we can easily mock the configuration data and test our services with different configurations in a controlled manner.

What sets Config.redacted apart from Config.string is its handling of sensitive information. It parses the config value and wraps it in a Redacted<string>, a data type designed for holding secrets.

When you use Console.log on a redacted, the actual value remains hidden, providing an added layer of security. The only way to access the value is by using Redacted.value.

1
import {
import Effect
Effect
,
import Config
Config
,
import Console
Console
,
import Redacted
Redacted
} from "effect"
2
3
const
const program: Effect.Effect<Redacted.Redacted<string>, ConfigError, never>
program
=
import Config
Config
.
const redacted: (name?: string) => Config.Config<Redacted.Redacted>

Constructs a config for a redacted value.

redacted
("API_KEY").
(method) Pipeable.pipe<Config.Config<Redacted.Redacted<string>>, Effect.Effect<Redacted.Redacted<string>, ConfigError, never>, Effect.Effect<Redacted.Redacted<string>, ConfigError, never>>(this: Config.Config<...>, ab: (_: Config.Config<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
4
import Effect
Effect
.
const tap: <Redacted.Redacted<string>, Effect.Effect<void, never, never>>(f: (a: Redacted.Redacted<string>) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+7 overloads)
tap
((
(parameter) redacted: Redacted.Redacted<string>
redacted
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
(`Console output: ${
(parameter) redacted: Redacted.Redacted<string>
redacted
}`)),
5
import Effect
Effect
.
const tap: <Redacted.Redacted<string>, Effect.Effect<void, never, never>>(f: (a: Redacted.Redacted<string>) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+7 overloads)
tap
((
(parameter) redacted: Redacted.Redacted<string>
redacted
) =>
6
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
(`Actual value: ${
import Redacted
Redacted
.
const value: <string>(self: Redacted.Redacted<string>) => string

Retrieves the original value from a `Redacted` instance. Use this function with caution, as it exposes the sensitive data.

value
(
(parameter) redacted: Redacted.Redacted<string>
redacted
)}`)
7
)
8
)
9
10
import Effect
Effect
.
const runSync: <Redacted.Redacted<string>, ConfigError>(effect: Effect.Effect<Redacted.Redacted<string>, ConfigError, never>) => Redacted.Redacted<...>
runSync
(
const program: Effect.Effect<Redacted.Redacted<string>, ConfigError, never>
program
)

If we run this program we will get the following output:

Terminal window
API_KEY=my-api-key tsx Redacted.ts
Console output: <redacted>
Actual value: my-api-key

In this example, you can see that when logging the redacted using Console.log, the actual value is replaced with <redacted>, ensuring that sensitive information is not exposed. The Redacted.value function, on the other hand, provides a controlled way to retrieve the original secret value.

Deprecated since version 3.3.0: Please use Config.redacted for handling sensitive information going forward.

Config.secret functions similarly to Config.redacted by securing sensitive information. It wraps configuration values in a Secret type, which also obscures details when logged but allows access through the Secret.value method.

1
import {
import Effect
Effect
,
import Config
Config
,
import Console
Console
,
import Secret
Secret
} from "effect"
2
3
const
const program: Effect.Effect<Secret.Secret, ConfigError, never>
program
=
import Config
Config
.
const secret: (name?: string) => Config.Config<Secret.Secret>

Constructs a config for a secret value.

secret
("API_KEY").
(method) Pipeable.pipe<Config.Config<Secret.Secret>, Effect.Effect<Secret.Secret, ConfigError, never>, Effect.Effect<Secret.Secret, ConfigError, never>>(this: Config.Config<...>, ab: (_: Config.Config<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
4
import Effect
Effect
.
const tap: <Secret.Secret, Effect.Effect<void, never, never>>(f: (a: Secret.Secret) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<Secret.Secret, E, R>) => Effect.Effect<...> (+7 overloads)
tap
((
(parameter) secret: Secret.Secret
secret
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
(`Console output: ${
(parameter) secret: Secret.Secret
secret
}`)),
5
import Effect
Effect
.
const tap: <Secret.Secret, Effect.Effect<void, never, never>>(f: (a: Secret.Secret) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<Secret.Secret, E, R>) => Effect.Effect<...> (+7 overloads)
tap
((
(parameter) secret: Secret.Secret
secret
) =>
6
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
(`Secret value: ${
import Secret
Secret
.
const value: (self: Secret.Secret) => string
value
(
(parameter) secret: Secret.Secret
secret
)}`)
7
)
8
)
9
10
import Effect
Effect
.
const runSync: <Secret.Secret, ConfigError>(effect: Effect.Effect<Secret.Secret, ConfigError, never>) => Secret.Secret
runSync
(
const program: Effect.Effect<Secret.Secret, ConfigError, never>
program
)

If we run this program we will get the following output:

Terminal window
API_KEY=my-api-key tsx Secret.ts
Console output: Secret(<redacted>)
Secret value: my-api-key