
Effect Ts
Load and validate app configuration and secrets in TypeScript services using Effect Config, Redacted, and custom ConfigProviders.
Overview
effect-ts is an agent skill most often used in Build (backend, also Operate infra) that teaches Effect Config, Redacted secrets, nested env keys, and custom ConfigProviders in TypeScript.
Install
npx skills add https://github.com/paulrberg/agent-skills --skill effect-tsWhat is this skill?
- Reads required and default-backed values with Config.number, Config.string, and Config.withDefault.
- Wraps secrets in Config.redacted and Redacted.value only at use sites to keep logs masked.
- Builds nested config via Config.nested (e.g. DATABASE_HOST, DATABASE_PORT) from grouped keys.
- Runs programs with Effect.gen and swaps env for tests using ConfigProvider.fromMap and Layer.setConfigProvider.
- Documents validation and transformation hooks on config pipelines.
- Demonstrates nested DATABASE_* env grouping via Config.nested
Adoption & trust: 1.8k installs on skills.sh; 62 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Effect service reads raw environment variables and risks leaking secrets or duplicating nested DATABASE_* parsing everywhere.
Who is it for?
Indie TypeScript backend authors standardizing on Effect for env and secrets at service startup.
Skip if: Plain Node apps with no Effect dependency or teams that only need a one-line dotenv call.
When should I use this skill?
Pull this in when reading configuration or environment variables via Effect's Config module, handling secrets with Redacted, providing custom config providers, or validating config values.
What do I get? / Deliverables
You get typed, testable config loading with Redacted masking and pluggable providers suitable for local, CI, and deployed runs.
- Typed config Effect program
- Layer or provider setup for tests vs runtime env
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Typed config wiring is introduced when building services but is revisited whenever env schemas or deploy targets change. Effect Config, nested env prefixes, and Redacted secrets are backend TypeScript infrastructure patterns.
Where it fits
Bootstrap an API server that loads PORT, HOST defaults, and redacted API_KEY through Effect.gen.
Audit startup paths so secrets stay Redacted in logs before deploy.
Swap ConfigProvider for staging vs production maps without touching route handlers.
How it compares
Effect-native config layering, not a generic twelve-factor env checklist or Zod-only .env parser.
Common Questions / FAQ
Who is effect-ts for?
Solo builders writing TypeScript backends or CLIs with Effect who need structured config, redacted secrets, and test doubles for environment variables.
When should I use effect-ts?
During Build (backend) when wiring PORT, API keys, and nested DATABASE_* variables; also in Operate (infra) when changing providers between local maps and deployment env without rewriting business logic.
Is effect-ts safe to install?
The skill describes secret-handling patterns; review the Security Audits panel on this page and never commit real keys—use Redacted and your host’s secret store.
SKILL.md
READMESKILL.md - Effect Ts
# Configuration & Environment Variables > When to read: pull this in when reading configuration or environment variables via Effect's `Config` module, handling secrets with `Redacted`, providing custom config providers, or validating config values. ```typescript import { Config, ConfigProvider, Effect, Layer, Redacted } from "effect" // Basic config values const port = Config.number("PORT") // Required number const host = Config.string("HOST").pipe( // Optional with default Config.withDefault("localhost") ) // Sensitive values (masked in logs) const apiKey = Config.redacted("API_KEY") // Returns Redacted<string> const secret = Redacted.value(yield* apiKey) // Unwrap when needed // Nested configuration with prefix const dbConfig = Config.all({ host: Config.string("HOST"), port: Config.number("PORT"), name: Config.string("NAME"), }).pipe(Config.nested("DATABASE")) // DATABASE_HOST, DATABASE_PORT, etc. // Using config in effects const program = Effect.gen(function* () { const p = yield* Config.number("PORT") const key = yield* Config.redacted("API_KEY") return { port: p, apiKey: Redacted.value(key) } }) // Custom config provider (e.g., from object instead of env) const customProvider = ConfigProvider.fromMap( new Map([["PORT", "3000"], ["API_KEY", "secret"]]) ) const withCustomConfig = Effect.provide( program, Layer.setConfigProvider(customProvider) ) // Config validation and transformation const validPort = Config.number("PORT").pipe( Config.validate({ message: "Port must be between 1 and 65535", validation: (n) => n >= 1 && n <= 65535, }) ) ``` # Critical Rules for Effect-TS These rules address common mistakes when working with Effect. Understanding why they matter helps write idiomatic Effect code. ## INEFFECTIVE: try-catch in Effect.gen **Avoid `try-catch` blocks inside `Effect.gen` generators for handling Effect failures.** Effect failures are returned as exits, not thrown as JavaScript exceptions. Using try-catch will not catch Effect failures—it only catches synchronous throws from non-Effect code. **Problematic:** ```typescript Effect.gen(function* () { try { const result = yield* someEffect; } catch (error) { // This catches synchronous throws only, NOT Effect failures // Effect failures bypass this entirely } }); ``` **Correct:** ```typescript Effect.gen(function* () { const result = yield* Effect.result(someEffect); if (result._tag === "Failure") { // Handle error case } }); ``` Alternative patterns: - `Effect.catchAll` / `Effect.catchTag` for error recovery - `Effect.result` to inspect success/failure - `Effect.tryPromise` / `Effect.try` for wrapping external code ## AVOID: Type Assertions **Avoid `as never`, `as any`, or `as unknown` type assertions.** These break TypeScript's type safety and hide real type errors. Always fix the underlying type issues instead. **Patterns to avoid:** ```typescript const value = something as any; const value = something as never; const value = something as unknown; ``` **Correct approach:** - Use proper generic type parameters - Import correct types from Effect - Use proper Effect constructors and combinators - Adjust function signatures to match usage Note: This is general TypeScript guidance. Occasional assertions may be justified when interfacing with poorly-typed external libraries, but document the reason. ## RECOMMENDED: return `yield*` for Errors **Use `return yield*` when yielding errors or interrupts in Effect.gen for clarity.** The runtime halts on failed yields regardless of `return`, but the explicit `return` makes termination obvious and prevents unreachable-code warnings. **Recommended:** ```typescript Effect.gen(function* () { if (someCondition) { return yield* Effect.fail("error message"); } if (shouldInterrupt) { return yield* Effect.interrupt; } const result = yield* someOtherEffect;