Documentation Index
Fetch the complete documentation index at: https://effect-ts-effect-smol.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
The Config module provides a declarative, type-safe way to load and validate configuration from various sources. It integrates seamlessly with the Schema module for runtime validation.
Overview
A Config<T> describes how to read and validate a value of type T from a ConfigProvider. Configs can be composed, transformed, and used directly as Effects.
Mental Model
- Config<T>: A recipe for extracting a typed value from a ConfigProvider
- ConfigProvider: The backing data source (environment variables, JSON files, etc.)
- ConfigError: Wraps either a SourceError (I/O failure) or SchemaError (validation failure)
- Yieldable: Every Config can be yielded inside Effect.gen
Reading Configuration
Basic Types
import { Config } from "effect"
// Read a string from environment
const host = Config.string("HOST")
// Read a number
const port = Config.number("PORT")
// Read an integer
const maxConnections = Config.int("MAX_CONNECTIONS")
// Read a boolean
const debugMode = Config.boolean("DEBUG")
Using Configs in Effects
import { Config, Effect } from "effect"
const program = Effect.gen(function*() {
const host = yield* Config.string("HOST")
const port = yield* Config.int("PORT")
yield* Effect.log(`Server will run on ${host}:${port}`)
return { host, port }
})
Structured Configuration
import { Config, Schema } from "effect"
// Define a config schema
const ServerConfig = Config.schema(
Schema.Struct({
host: Schema.String,
port: Schema.Int,
ssl: Schema.Boolean
}),
"server" // prefix for config keys
)
// Use in an Effect
const program = Effect.gen(function*() {
const config = yield* ServerConfig
console.log(config)
// { host: "localhost", port: 8080, ssl: false }
})
Default Values and Optional Configs
With Default
import { Config } from "effect"
const port = Config.int("PORT").pipe(
Config.withDefault(3000)
)
const logLevel = Config.string("LOG_LEVEL").pipe(
Config.withDefault("info")
)
Optional Configuration
import { Config, Effect, Option } from "effect"
const apiKey = Config.option(Config.string("API_KEY"))
const program = Effect.gen(function*() {
const key = yield* apiKey
if (Option.isSome(key)) {
yield* Effect.log("API key is configured")
} else {
yield* Effect.log("No API key provided")
}
})
Specialized Config Types
Duration
import { Config } from "effect"
const timeout = Config.duration("REQUEST_TIMEOUT")
// Accepts: "30s", "5m", "1h", etc.
URL
import { Config } from "effect"
const apiUrl = Config.url("API_URL")
// Validates URL format
Redacted (for secrets)
import { Config } from "effect"
const apiSecret = Config.redacted("API_SECRET")
// Value is redacted in logs and error messages
Date
import { Config } from "effect"
const deploymentDate = Config.date("DEPLOYMENT_DATE")
// Parses ISO date strings
Map
import { Config } from "effect"
const upperCaseHost = Config.string("HOST").pipe(
Config.map((s) => s.toUpperCase())
)
MapOrFail
import { Config, Effect } from "effect"
const validatedPort = Config.int("PORT").pipe(
Config.mapOrFail((port) =>
port >= 1024 && port <= 65535
? Effect.succeed(port)
: Effect.fail(
new Config.ConfigError(
new ConfigProvider.SourceError("Port must be between 1024 and 65535")
)
)
)
)
Combining Configs
Using Config.all
import { Config } from "effect"
const appConfig = Config.all({
host: Config.string("HOST"),
port: Config.int("PORT"),
database: Config.string("DATABASE_URL")
})
// Returns Config<{ host: string, port: number, database: string }>
Sequential Composition
import { Config, Effect } from "effect"
const connectionString = Effect.gen(function*() {
const host = yield* Config.string("DB_HOST")
const port = yield* Config.int("DB_PORT")
const database = yield* Config.string("DB_NAME")
return `postgresql://${host}:${port}/${database}`
})
Error Handling
OrElse
import { Config } from "effect"
const config = Config.string("PRIMARY_HOST").pipe(
Config.orElse(() => Config.string("FALLBACK_HOST"))
)
Catching Errors
import { Config, Effect } from "effect"
const program = Effect.gen(function*() {
const port = yield* Config.int("PORT")
return port
}).pipe(
Effect.catchTag("ConfigError", (error) =>
Effect.succeed(3000)
)
)
Config Providers
From Environment Variables
import { ConfigProvider, Effect } from "effect"
const provider = ConfigProvider.fromEnv({
env: process.env
})
const program = Effect.gen(function*() {
const host = yield* Config.string("HOST")
return host
}).pipe(
Effect.provide(ConfigProvider.layer(provider))
)
From JSON Object
import { ConfigProvider } from "effect"
const provider = ConfigProvider.fromUnknown({
host: "localhost",
port: 8080,
database: {
url: "postgresql://localhost/mydb"
}
})
Nested Configuration
import { Config, ConfigProvider, Effect } from "effect"
const databaseConfig = Config.schema(
Schema.Struct({
url: Schema.String,
poolSize: Schema.Int
}),
"database"
)
const provider = ConfigProvider.fromUnknown({
database_url: "postgresql://localhost/mydb",
database_poolSize: 10
})
Integration with Layers
import { Config, Effect, Layer, ServiceMap } from "effect"
class DatabaseConfig extends ServiceMap.Service<DatabaseConfig, {
url: string
poolSize: number
}>()("DatabaseConfig") {
static readonly layer = Layer.effect(
DatabaseConfig,
Effect.gen(function*() {
const url = yield* Config.string("DATABASE_URL")
const poolSize = yield* Config.int("DATABASE_POOL_SIZE").pipe(
Config.withDefault(10)
)
return DatabaseConfig.of({ url, poolSize })
})
)
}
Best Practices
- Validate at startup: Load and validate all configuration at application startup
- Use Schema for complex configs: Leverage Schema.Struct for structured configuration
- Provide defaults: Use withDefault for non-critical configuration
- Keep secrets redacted: Use Config.redacted for sensitive values
- Group related configs: Use Config.all or Schema.Struct to group related settings
- Use layers for config services: Wrap configuration in layers for dependency injection
Common Patterns
Environment-Specific Configuration
import { Config, Effect } from "effect"
const getConfig = Effect.gen(function*() {
const env = yield* Config.string("NODE_ENV").pipe(
Config.withDefault("development")
)
if (env === "production") {
return yield* productionConfig
} else {
return yield* developmentConfig
}
})
Required vs Optional
import { Config } from "effect"
// Required - will fail if missing
const required = Config.string("REQUIRED_KEY")
// Optional - returns Option
const optional = Config.option(Config.string("OPTIONAL_KEY"))
// Optional with default
const withDefault = Config.string("KEY").pipe(
Config.withDefault("default-value")
)
Next Steps
- Learn about Schema for validation
- Explore Layer for dependency injection
- Understand Effect for error handling