better-env better-env Docs

Schema Helpers

Utility functions for adding defaults, marking values as redacted, and parsing JSON environment variables.

better-env provides three helper functions for common patterns when defining environment variable schemas.

import { withDefault, redacted, json } from "@ayronforge/better-env"

withDefault

Adds a default value to a schema, making the variable optional. If the env var is missing, the default value is used instead.

withDefault supports both data-first and pipe-style usage:

Data-first style

import { withDefault, port } from "@ayronforge/better-env"

createEnv({
  server: {
    PORT: withDefault(port, 3000),
    // If PORT is not set, defaults to 3000
  },
})

Pipe style

import { withDefault, port } from "@ayronforge/better-env"

createEnv({
  server: {
    PORT: port.pipe(withDefault(3000)),
  },
})

Both styles produce the same result. Use whichever reads better in your codebase.

Note

withDefault wraps the schema in Schema.UndefinedOr(...) and applies a transform that substitutes undefined with the default value. The resulting type reflects the schema’s output type — e.g., withDefault(port, 3000) produces number, not number | undefined.

redacted

Wraps a schema with Effect’s Redacted type. This prevents accidental logging, serialization, or spreading of sensitive values.

import { redacted } from "@ayronforge/better-env"
import { Redacted, Schema } from "effect"

const env = createEnv({
  server: {
    API_SECRET: redacted(Schema.String),
    // Equivalent to: Schema.Redacted(Schema.String)
  },
})

// env.API_SECRET is Redacted<string>
console.log(env.API_SECRET)  // <redacted>
JSON.stringify(env)           // {"API_SECRET":"<redacted>"}

// Explicitly unwrap when you need the plain value
const secret: string = Redacted.value(env.API_SECRET)

The redacted helper is a shorthand for Schema.Redacted(schema). Values wrapped in Redacted appear as <redacted> in logs and serialized output, and remain wrapped when spread or iterated.

json

Parses a JSON string env var and validates the parsed result against an inner schema:

import { json } from "@ayronforge/better-env"
import { Schema } from "effect"

createEnv({
  server: {
    // FEATURE_FLAGS='{"darkMode":true,"newUI":false}'
    FEATURE_FLAGS: json(Schema.Struct({
      darkMode: Schema.Boolean,
      newUI: Schema.Boolean,
    })),
  },
})

The json helper is equivalent to Schema.parseJson(schema). It first parses the string as JSON, then validates the result against the provided schema.

Validation behavior

If the env var is not valid JSON, or if the parsed JSON doesn’t match the inner schema, validation fails:

FEATURE_FLAGS: Expected valid JSON, but got '{ invalid json'

Composing helpers

Helpers can be composed together:

import { withDefault, redacted, json } from "@ayronforge/better-env"
import { Schema } from "effect"

createEnv({
  server: {
    // Optional JSON config with a default
    APP_CONFIG: withDefault(
      json(Schema.Struct({ debug: Schema.Boolean })),
      { debug: false },
    ),

    // Redacted string with a default
    API_KEY: redacted(Schema.String).pipe(withDefault("dev-key")),
  },
})