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 several built-in types for configuration values, which you can use right 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.
urlConstructs a config for an URL value.

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

Example (Using Primitives)

Here’s an example of loading a basic configuration using environment variables for HOST and PORT:

primitives.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Config
Config
} from "effect"
// Define a program that loads HOST and PORT configuration
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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)

@since2.0.0

gen
(function* () {
// Load the HOST from environment variables
const
const host: string
host
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

@since2.0.0

string
("HOST")
// Load the PORT as a number
const
const port: number
port
= yield*
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

@since2.0.0

number
("PORT")
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(`Application started: ${
const host: string
host
}:${
const port: number
port
}`)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
const program: Effect.Effect<void, ConfigError, never>
program
)

If you run this without setting the required environment variables:

Terminal window
npx tsx primitives.ts

you’ll see an error indicating the missing configuration:

Terminal window
[Error: (Missing data at HOST: "Expected HOST to exist in the process context")] {
name: '(FiberFailure) Error',
[Symbol(effect/Runtime/FiberFailure)]: Symbol(effect/Runtime/FiberFailure),
[Symbol(effect/Runtime/FiberFailure/Cause)]: {
_tag: 'Fail',
error: {
_op: 'MissingData',
path: [ 'HOST' ],
message: 'Expected HOST to exist in the process context'
}
}
}

To run the program successfully, set the environment variables as shown below:

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

Output:

Terminal window
Application started: localhost:8080

Sometimes, you may encounter situations where an environment variable is missing, leading to an incomplete configuration. To address this, Effect provides the Config.withDefault function, which allows you to specify a default value. This fallback ensures that your application continues to function even if a required environment variable is not set.

Example (Using Default Values)

defaults.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Config
Config
} from "effect"
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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)

@since2.0.0

gen
(function* () {
const
const host: string
host
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

@since2.0.0

string
("HOST")
// Use default 8080 if PORT is not set
const
const port: number
port
= yield*
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

@since2.0.0

number
("PORT").
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.

@since2.0.0

withDefault
(8080))
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(`Application started: ${
const host: string
host
}:${
const port: number
port
}`)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
const program: Effect.Effect<void, ConfigError, never>
program
)

Running this program with only the HOST environment variable set:

Terminal window
HOST=localhost npx tsx defaults.ts

produces the following output:

Terminal window
Application started: localhost:8080

In this case, even though the PORT environment variable is not set, the program continues to run, using the default value of 8080 for the port. This ensures that the application remains functional without requiring every configuration to be explicitly provided.

Effect provides several built-in constructors that allow you to define and manipulate configurations. These constructors take a Config as input and produce another Config, enabling more complex configuration structures.

Here are some of the key constructors:

ConstructorDescription
arrayConstructs a configuration for an array of values.
chunkConstructs a configuration for a sequence of values.
optionReturns an optional configuration. If the data is missing, the result will be None; otherwise, it will be Some.
repeatDescribes a sequence of values, each following the structure of the given config.
hashSetConstructs a configuration for a set of values.
hashMapConstructs a configuration for a key-value map.

Additionally, there are three special constructors for specific use cases:

ConstructorDescription
succeedConstructs a config that contains a predefined value.
failConstructs a config that fails with the specified error message.
allCombines multiple configurations into a tuple, struct, or argument list.

Example (Using array Constructor)

The following example demonstrates how to load an environment variable as an array of strings using the Config.array constructor.

array.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Config
Config
} from "effect"
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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)

@since2.0.0

gen
(function* () {
// Load array from env var MY_ARRAY
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.

@since2.0.0

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

Constructs a config for a string value.

@since2.0.0

string
(), "MY_ARRAY")
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(
const config: string[]
config
)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
const program: Effect.Effect<void, ConfigError, never>
program
)

If we run this program with the following environment variable:

Terminal window
MY_ARRAY=a,b,c npx tsx array.ts

The output will be:

Terminal window
[ 'a', 'b', 'c' ]

This shows how the array constructor converts a comma-separated string from an environment variable into an array of values, making configuration handling more flexible.

Effect provides several built-in operators to work with configurations, allowing you to manipulate and transform them according to your needs.

These operators enable you to modify configurations or validate their values:

OperatorDescription
validateEnsures that a configuration meets certain criteria, returning a validation error if it does not.
mapTransforms the values of a configuration using a provided function.
mapAttemptSimilar to map, but catches any errors thrown by the function and converts them into validation errors.
mapOrFailLike map, but the function can fail. If it does, the result is a validation error.

Example (Using validate Operator)

validate.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Config
Config
} from "effect"
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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)

@since2.0.0

gen
(function* () {
// Load the NAME environment variable and validate its length
const
const config: string
config
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

@since2.0.0

string
("NAME").
Pipeable.pipe<Config.Config<string>, Config.Config<string>>(this: Config.Config<string>, ab: (_: Config.Config<string>) => Config.Config<string>): Config.Config<...> (+21 overloads)
pipe
(
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.

@since2.0.0

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

Returns the length of a String object.

length
>= 4
})
)
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(
const config: string
config
)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
const program: Effect.Effect<void, ConfigError, never>
program
)

If we run this program with an invalid NAME value:

Terminal window
NAME=foo npx tsx validate.ts

The output will be:

Terminal window
[Error: (Invalid data at NAME: "Expected a string at least 4 characters long")] {
name: '(FiberFailure) Error',
[Symbol(effect/Runtime/FiberFailure)]: Symbol(effect/Runtime/FiberFailure),
[Symbol(effect/Runtime/FiberFailure/Cause)]: {
_tag: 'Fail',
error: {
_op: 'InvalidData',
path: [ 'NAME' ],
message: 'Expected a string at least 4 characters long'
}
}
}

Fallback operators are useful when you want to provide alternative configurations in case of errors or missing data. These operators ensure that your program can still run even if some configuration values are unavailable.

OperatorDescription
orElseAttempts to use the primary config first. If it fails or is missing, it falls back to another config.
orElseIfSimilar to orElse, but it switches to the fallback config only if the error matches a condition.

Example (Using orElse for Fallback)

In this example, the program requires two configuration values: A and B. We set up two configuration providers, each containing only one of the required values. Using the orElse operator, we combine these providers so the program can retrieve both A and B.

orElse.ts
import {
import Config
Config
,
import ConfigProvider
ConfigProvider
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Layer
Layer
} from "effect"
// A program that requires two configurations: A and B
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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)

@since2.0.0

gen
(function* () {
const
const A: string
A
= yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

@since2.0.0

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

Constructs a config for a string value.

@since2.0.0

string
("B") // Retrieve config B
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(`A: ${
const A: string
A
}, B: ${
const B: string
B
}`)
})
// First provider has A but is missing B
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.

@since2.0.0

fromMap
(new
var Map: MapConstructor
new <string, string>(iterable?: Iterable<readonly [string, string]> | null | undefined) => Map<string, string> (+3 overloads)
Map
([["A", "A"]]))
// Second provider has B but is missing A
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.

@since2.0.0

fromMap
(new
var Map: MapConstructor
new <string, string>(iterable?: Iterable<readonly [string, string]> | null | undefined) => Map<string, string> (+3 overloads)
Map
([["B", "B"]]))
// Use `orElse` to fall back from provider1 to provider2
const
const layer: Layer.Layer<never, never, never>
layer
=
import Layer
Layer
.
const setConfigProvider: (configProvider: ConfigProvider.ConfigProvider) => Layer.Layer<never>

Sets the current ConfigProvider.

@since2.0.0

setConfigProvider
(
const provider1: ConfigProvider.ConfigProvider
provider1
.
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.

@since2.0.0

orElse
(() =>
const provider2: ConfigProvider.ConfigProvider
provider2
))
)
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

@since2.0.0

provide
(
const program: Effect.Effect<void, ConfigError, never>
program
,
const layer: Layer.Layer<never, never, never>
layer
))

If we run this program:

Terminal window
npx tsx orElse.ts

The output will be:

Terminal window
A: A, B: B

Effect allows you to define configurations for custom types by combining primitive configs using Config operators (such as zip, orElse, map) and constructors (like array, hashSet).

For example, let’s create a HostPort class, which has two fields: host and port.

class
class HostPort
HostPort
{
constructor(readonly
HostPort.host: string
host
: string, readonly
HostPort.port: number
port
: number) {}
get
HostPort.url: string
url
() {
return `${this.
HostPort.host: string
host
}:${this.
HostPort.port: number
port
}`
}
}

To define a configuration for this custom type, we can combine primitive configs for string and number:

import {
import Config
Config
} from "effect"
class
class HostPort
HostPort
{
constructor(readonly
HostPort.host: string
host
: string, readonly
HostPort.port: number
port
: number) {}
get
HostPort.url: string
url
() {
return `${this.
HostPort.host: string
host
}:${this.
HostPort.port: number
port
}`
}
}
// Combine the configuration for 'HOST' and 'PORT'
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.

@since2.0.0

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

Constructs a config for a string value.

@since2.0.0

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

Constructs a config for a float value.

@since2.0.0

number
("PORT")])
// Map the configuration values into a HostPort instance
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.

@since2.0.0

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

In this example, Config.all(configs) combines two primitive configurations, Config<string> and Config<number>, into a Config<[string, number]>. The Config.map operator is then used to transform these values into an instance of the HostPort class.

Example (Using Custom Configuration)

App.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Config
Config
} from "effect"
class
class HostPort
HostPort
{
constructor(readonly
HostPort.host: string
host
: string, readonly
HostPort.port: number
port
: number) {}
get
HostPort.url: string
url
() {
return `${this.
HostPort.host: string
host
}:${this.
HostPort.port: number
port
}`
}
}
// Combine the configuration for 'HOST' and 'PORT'
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.

@since2.0.0

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

Constructs a config for a string value.

@since2.0.0

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

Constructs a config for a float value.

@since2.0.0

number
("PORT")])
// Map the configuration values into a HostPort instance
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.

@since2.0.0

map
(
const both: Config.Config<[string, number]>
both
,
([
host: string
host
,
port: number
port
]) => new
constructor HostPort(host: string, port: number): HostPort
HostPort
(
host: string
host
,
port: number
port
)
)
// Main program that reads configuration and starts the application
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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)

@since2.0.0

gen
(function* () {
const
const hostPort: HostPort
hostPort
= yield*
const config: Config.Config<HostPort>
config
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

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

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
const program: Effect.Effect<void, ConfigError, never>
program
)

When you run this program, it will try to retrieve the values for HOST and PORT from your environment variables:

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

If successful, it will print:

Terminal window
Application started: localhost:8080

We’ve seen how to define configurations at the top level, whether for primitive or custom types. In some cases, though, you might want to structure your configurations in a more nested way, organizing them under common namespaces for clarity and manageability.

For instance, consider the following ServiceConfig type:

class
class ServiceConfig
ServiceConfig
{
constructor(
readonly
ServiceConfig.host: string
host
: string,
readonly
ServiceConfig.port: number
port
: number,
readonly
ServiceConfig.timeout: number
timeout
: number
) {}
get
ServiceConfig.url: string
url
() {
return `${this.
ServiceConfig.host: string
host
}:${this.
ServiceConfig.port: number
port
}`
}
}

If you were to use this configuration in your application, it would expect the HOST, PORT, and TIMEOUT environment variables at the top level. But in many cases, you may want to organize configurations under a shared namespace—for example, grouping HOST and PORT under a SERVER namespace, while keeping TIMEOUT at the root.

To do this, you can use the Config.nested operator, which allows you to nest configuration values under a specific namespace. Let’s update the previous example to reflect this:

import {
import Config
Config
} from "effect"
class
class ServiceConfig
ServiceConfig
{
constructor(
readonly
ServiceConfig.host: string
host
: string,
readonly
ServiceConfig.port: number
port
: number,
readonly
ServiceConfig.timeout: number
timeout
: number
) {}
get
ServiceConfig.url: string
url
() {
return `${this.
ServiceConfig.host: string
host
}:${this.
ServiceConfig.port: number
port
}`
}
}
const
const serverConfig: Config.Config<[string, number]>
serverConfig
=
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.

@since2.0.0

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

Constructs a config for a string value.

@since2.0.0

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

Constructs a config for a float value.

@since2.0.0

number
("PORT")
])
const
const serviceConfig: Config.Config<ServiceConfig>
serviceConfig
=
import Config
Config
.
const map: <[[string, number], number], ServiceConfig>(self: Config.Config<[[string, number], number]>, f: (a: [[string, number], 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.

@since2.0.0

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

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

@since2.0.0

all
([
// Read 'HOST' and 'PORT' from 'SERVER' namespace
import Config
Config
.
const nested: <[string, number]>(self: Config.Config<[string, number]>, name: string) => Config.Config<[string, number]> (+1 overload)

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

@since2.0.0

nested
(
const serverConfig: Config.Config<[string, number]>
serverConfig
, "SERVER"),
// Read 'TIMEOUT' from the root namespace
import Config
Config
.
const number: (name?: string) => Config.Config<number>

Constructs a config for a float value.

@since2.0.0

number
("TIMEOUT")
]),
([[
host: string
host
,
port: number
port
],
timeout: number
timeout
]) => new
constructor ServiceConfig(host: string, port: number, timeout: number): ServiceConfig
ServiceConfig
(
host: string
host
,
port: number
port
,
timeout: number
timeout
)
)

Now, if you run your application with this configuration setup, it will look for the following environment variables:

  • SERVER_HOST for the host value
  • SERVER_PORT for the port value
  • TIMEOUT for the timeout value

This structured approach keeps your configuration more organized, especially when dealing with multiple services or complex applications.

When testing services, there are times when you need to provide specific configurations for your tests. To simulate this, it’s useful to mock the configuration backend that reads these values.

You can achieve this using the ConfigProvider.fromMap constructor. This method allows you to create a configuration provider from a Map<string, string>, where the map represents the configuration data. You can then use this mock provider in place of the default one by calling Layer.setConfigProvider. This function returns a Layer that can override the default configuration for your tests.

Example (Mocking a Config Provider for Testing)

import {
import Config
Config
,
import ConfigProvider
ConfigProvider
,
import Layer
Layer
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
class
class HostPort
HostPort
{
constructor(readonly
HostPort.host: string
host
: string, readonly
HostPort.port: number
port
: number) {}
get
HostPort.url: string
url
() {
return `${this.
HostPort.host: string
host
}:${this.
HostPort.port: number
port
}`
}
}
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.

@since2.0.0

map
(
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.

@since2.0.0

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

Constructs a config for a string value.

@since2.0.0

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

Constructs a config for a float value.

@since2.0.0

number
("PORT")]),
([
host: string
host
,
port: number
port
]) => new
constructor HostPort(host: string, port: number): HostPort
HostPort
(
host: string
host
,
port: number
port
)
)
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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)

@since2.0.0

gen
(function* () {
const
const hostPort: HostPort
hostPort
= yield*
const config: Config.Config<HostPort>
config
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(`Application started: ${
const hostPort: HostPort
hostPort
.
HostPort.url: string
url
}`)
})
// Create a mock config provider using a map with test data
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.

@since2.0.0

fromMap
(
new
var Map: MapConstructor
new <string, string>(iterable?: Iterable<readonly [string, string]> | null | undefined) => Map<string, string> (+3 overloads)
Map
([
["HOST", "localhost"],
["PORT", "8080"]
])
)
// Override the default config provider with the mock provider
const
const layer: Layer.Layer<never, never, never>
layer
=
import Layer
Layer
.
const setConfigProvider: (configProvider: ConfigProvider.ConfigProvider) => Layer.Layer<never>

Sets the current ConfigProvider.

@since2.0.0

setConfigProvider
(
const mockConfigProvider: ConfigProvider.ConfigProvider
mockConfigProvider
)
// Run the program using the mock config provider
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

@since2.0.0

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

This approach helps you create isolated tests that don’t rely on external environment variables, ensuring your tests run consistently with mock configurations.

The Config.redacted function is used to handle sensitive information safely. It parses the configuration value and wraps it in a Redacted<string>, a specialized data type designed to protect secrets.

When you log a Redacted value using console.log, the actual content remains hidden, providing an extra layer of security. To access the real value, you must explicitly use Redacted.value.

Example (Handling Redacted Values)

redacted.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Config
Config
,
import Redacted
Redacted
} from "effect"
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Redacted.Redacted<string>, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const
const redacted: Redacted.Redacted<string>
redacted
= yield*
import Config
Config
.
const redacted: (name?: string) => Config.Config<Redacted.Redacted>

Constructs a config for a redacted value.

@since2.0.0

redacted
("API_KEY")
// Log the redacted value, which won't reveal the actual secret
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(`Console output: ${
const redacted: Redacted.Redacted<string>
redacted
}`)
// Access the real value using Redacted.value and log it
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

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.

@example

import { Redacted } from "effect"

const API_KEY = Redacted.make("1234567890")

assert.equal(Redacted.value(API_KEY), "1234567890")

@since3.3.0

value
(
const redacted: Redacted.Redacted<string>
redacted
)}`)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
const program: Effect.Effect<void, ConfigError, never>
program
)

When this program is executed:

Terminal window
API_KEY=my-api-key tsx redacted.ts

The output will look like this:

Terminal window
Console output: <redacted>
Actual value: my-api-key

As shown, when logging the Redacted value using console.log, the output is <redacted>, ensuring that sensitive data remains concealed. However, by using Redacted.value, the true value ("my-api-key") can be accessed and displayed, providing controlled access to the secret.

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

The Config.secret function was previously used to secure sensitive information in a similar way to Config.redacted. It wraps configuration values in a Secret type, which also conceals details when logged but allows access via Secret.value.

Example (Using Deprecated Config.secret)

secret.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Config
Config
,
import Secret
Secret
} from "effect"
const
const program: Effect.Effect<void, ConfigError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Secret.Secret, ConfigError, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Secret.Secret, ConfigError, never>>, void, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const
const secret: Secret.Secret
secret
= yield*
import Config
Config
.
const secret: (name?: string) => Config.Config<Secret.Secret>

Constructs a config for a secret value.

@since2.0.0

@deprecated

secret
("API_KEY")
// Log the secret value, which won't reveal the actual secret
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(`Console output: ${
const secret: Secret.Secret
secret
}`)
// Access the real value using Secret.value and log it
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(`Actual value: ${
import Secret
Secret
.
const value: (self: Secret.Secret) => string

@since2.0.0

@deprecated

value
(
const secret: Secret.Secret
secret
)}`)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, ConfigError>(effect: Effect.Effect<void, ConfigError, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
const program: Effect.Effect<void, ConfigError, never>
program
)

When this program is executed:

Terminal window
API_KEY=my-api-key tsx secret.ts

The output will look like this:

Terminal window
Console output: Secret(<redacted>)
Actual value: my-api-key