Skip to content

Getting started

This quick start gets you productive fast. Define a couple of settings, wire them into a proxy, and you’re off. Inline links point to deeper docs when you want more.

Installation

bash
npm install envarna

Basic Usage

Create settings classes

Settings are grouped into classes by concern. Start small, then add a richer example.

ts
// settings/app.ts
import { BaseSettings, setting, v } from 'envarna'

export class AppSettings extends BaseSettings {
  @setting.string()
  name: string = 'MyApp'

  @setting(v.enum(['debug','info','warn','error']))
  logLevel: string = 'info'
}
ts
// settings/smtp.ts
import { BaseSettings, setting, secret, v } from 'envarna'

export class SmtpSettings extends BaseSettings {
  @setting.string()
  host: string = 'localhost'

  @setting.number()
  port: number = 25

  @setting.string()
  from: string = 'noreply@example.org'

  // Credentials are optional individually, but must be set together
  @setting(v.string().optional())
  username?: string

  @setting(v.string().optional())
  @secret()
  password?: string

  // Cross‑field check: both username and password or neither
  protected override validate(): void {
    const u = this.username !== undefined
    const p = this.password !== undefined
    if (u !== p) {
      throw new Error('Both username and password must be set together or left undefined.')
    }
  }
}

Collect your settings into a proxy

Use createSettingsProxy() to define a centralized settings object. Pass classes for full IntelliSense. This pattern supports lazy loading and async overrides.

ts
// settings/index.ts
import { createSettingsProxy } from 'envarna'
import { AppSettings } from './app'
import { SmtpSettings } from './smtp'

export const settings = createSettingsProxy({
  app: AppSettings,
  smtp: SmtpSettings,
})

Use your settings in application code

ts
// main.ts
import { settings } from './settings'

console.log(`[${settings.app.name}] SMTP → ${settings.smtp.host}:${settings.smtp.port}`)

Provide environment variables

Envarna loads values from environment variables. Names are derived from:

  • The class name with Settings stripped and uppercased: AppSettingsAPP_, SmtpSettingsSMTP_
  • The field name uppercased with underscores: logLevelLOG_LEVEL, fromFROM
  • Combined: APP_NAME, APP_LOG_LEVEL, SMTP_HOST, SMTP_PORT, SMTP_FROM

By default, envarna reads variables first from process.env, then from a local .env (if present) via dotenv.

plaintext
APP_NAME=My Worker
APP_LOG_LEVEL=info
SMTP_HOST=mail.example.org
SMTP_PORT=587
SMTP_FROM=noreply@example.org
# SMTP_USERNAME and SMTP_PASSWORD are optional but must be set together

If a required variable is missing (and no default is provided) or is malformed, envarna throws a validation error at startup.

Beyond the basics

Use validators for constraints, aliases for naming, arrays/objects for structure, and a proxy for composition. Here are quick examples with links to more.

ts
// Validation with v (min/max, enum, email)
import { BaseSettings, setting, v, alias } from 'envarna';

export class ServiceSettings extends BaseSettings {
  @setting(v.number().int().min(1).max(10).default(5))
  retries!: number;

  @setting(v.enum(['debug','info','warn','error']))
  logLevel!: string;

  @setting(v.string().email())
  contact!: string;

  @setting(v.url())
  @alias('ACME_API_URL')
  endpoint!: string;
}

// Arrays and objects (JSON from env)
export class DataSettings extends BaseSettings {
  @setting(v.array(v.number()))
  weights!: number[];              // DATA_WEIGHTS=[1,2,3]

  @setting.object({ name: v.string(), age: v.number() })
  user!: { name: string; age: number }; // DATA_USER={"name":"Ada","age":42}
}

// Secrets and redaction (safe in JSON dumps)
import { secret } from 'envarna'
export class DbSettings extends BaseSettings {
  @setting.string()
  @secret()
  connectionString!: string;
}

// Testing: override values per test (no process.env mutation)
// DbSettings.overrideForTest({ connectionString: 'mongodb://test' })
// DbSettings.clearOverride()

// Settings proxy: compose classes with lazy/async loaders
import { createSettingsProxy } from 'envarna'
export const settings = createSettingsProxy({
  service: ServiceSettings,
  data: DataSettings,
  db: async () => {
    const s = DbSettings.load()
    // e.g., await fetch secret here if needed
    return s
  },
})

See more:

What is v?

v builds Zod validators with environment‑friendly coercion (strings → numbers/booleans/dates, JSON arrays/objects). Use it when you need constraints:

ts
@setting(v.number().int().min(10).max(100).default(42))

Prefer v.* for env safety; you can still reach for raw Zod if needed.

Discover and Document Your Settings

Once your settings classes are defined, you can explore and document them with envarna's CLI.

List all known settings:

bash
npx envarna list

Generate a .env.template of required/optional variables:

bash
npx envarna env > .env.template

More outputs:

bash
npx envarna json --root cfg --flat --code
npx envarna yaml --root cfg --flat --code
npx envarna values        # Helm values.yaml
npx envarna compose       # docker-compose environment:
npx envarna k8s           # Kubernetes env: list

This helps you avoid missing variables and makes it easier to onboard new developers or deploy to new environments.

What’s Next?

For many projects, this is all you need. When you need more, explore:

Released under the MIT License.