npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zerly/config

v0.8.0

Published

[![npm version](https://img.shields.io/npm/v/@zerly/config.svg)](https://www.npmjs.com/package/@zerly/config)

Readme

@zerly/config

npm version

Type-safe configuration module for NestJS with support for dotenv and YAML sources, automatic example file generation, and validation.

Installation

pnpm add @zerly/config

Features

  • Dual format support: process.env (dotenv) and YAML files
  • @Env() decorator for declarative config field mapping
  • ConfigBuilder with fluent API, lazy evaluation, and custom validation
  • Automatic .env.example / env.example.yaml generation (pre-DI, survives crashes)
  • Frozen config objects (immutable after creation)
  • Smart app root resolution for monorepo setups (Nx, pnpm workspaces)
  • Full @nestjs/config compatibility (ConfigService.get() works as usual)

Quick Start

1. Define a config class

import { Env, ConfigBuilder } from '@zerly/config';

const DB_CONFIG = Symbol('db-config');

class DbConfig {
  @Env('database.host', { default: 'localhost' })
  host!: string;

  @Env('database.port', { type: Number, default: 5432 })
  port!: number;

  @Env('database.name', { description: 'Database name' })
  name!: string;
}

export const dbConfig = ConfigBuilder
  .from(DbConfig, DB_CONFIG)
  .validate(myValidator) // typia, zod, or any (config) => config function
  .build();

2. Register in your module

import { Module } from '@nestjs/common';
import { ConfigModule } from '@zerly/config';
import { dbConfig } from './configs/db.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      format: 'yaml',           // or omit for dotenv (default)
      path: 'config/app.yaml',  // default YAML file path (optional)
      load: [dbConfig],
    }),
  ],
})
export class AppModule {}

3. Use in services

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class DbService {
  constructor(private readonly config: ConfigService) {
    const db = this.config.getOrThrow<DbConfig>(DB_CONFIG);
    console.log(`Connecting to ${db.host}:${db.port}`);
  }
}

Formats

Dotenv (default)

Reads from process.env. Keys are flat env var names:

@Env('DATABASE_PORT', { type: Number, default: 5432 })
port!: number;

YAML

Reads from YAML files. Keys are dot-separated paths:

@Env('database.port', { type: Number, default: 5432 })
port!: number;
# env.yaml
database:
  port: 5432
  host: localhost

The format is set once at module level — no mixing within one app.

API

ConfigModule.forRoot(options?)

Registers configuration globally.

interface IConfigModuleOptions {
  /** Config format: 'dotenv' (default) or 'yaml'. */
  format?: ConfigFormat;

  /** Default YAML file path. Defaults to 'env.yaml' resolved from app root. */
  path?: string;

  /** Config factories to load. Accepts ConfigFactory or { path, config } objects. */
  load?: Array<ConfigFactory | IConfigLoadItem>;

  /** Output directory for example file generation (useful in monorepos). */
  outputDir?: string;
}

ConfigModule.forFeature(configOrOptions)

Registers feature-specific configuration.

// Simple
ConfigModule.forFeature(notificationsConfig)

// With custom YAML path
ConfigModule.forFeature({
  path: 'config/notifications.yaml',
  config: notificationsConfig,
})

@Env(key, options?)

Property decorator mapping a config field to a key.

| Option | Type | Description | |---------------|------------------------------------------------|--------------------------------------------------------------| | type | String \| Number \| Boolean \| Array \| Enum | Type conversion (dotenv only; YAML values are already typed) | | default | T | Fallback value if key is missing | | example | T | Example value for generated files | | description | string | Inline comment in generated example | | comment | string | Additional comment |

ConfigBuilder.from(Class, token).validate(fn).build()

Fluent builder producing a ConfigFactory for @nestjs/config. Resolution is lazy — values are read when NestJS DI invokes the factory, not at import time.

Per-config YAML paths

Each config can read from its own YAML file:

ConfigModule.forRoot({
  format: 'yaml',
  path: 'config/app.yaml',              // default for configs without own path
  load: [
    appConfig,                            // uses default path
    { path: 'config/db.yaml', config: dbConfig },  // own file
  ],
})

Example file generation

On startup, the module generates .env.example (dotenv) or env.example.yaml (YAML) synchronously before DI resolution — so the file exists even if the app crashes on validation.

  • Content is written only when the SHA-256 hash changes
  • Errors during generation are logged as warnings, never block startup
  • In dotenv mode, a secondary async pass runs post-DI with resolved runtime values

Array support

// YAML — native arrays
@Env('database.replicas', {
  type: Array,
  example: [{ host: 'replica1', port: 5432 }],
})
replicas!: IReplica[];

// Dotenv — JSON string in env var
// DATABASE_REPLICAS='[{"host":"replica1","port":5432}]'
@Env('DATABASE_REPLICAS', { type: Array })
replicas!: IReplica[];

Monorepo support

In Nx / pnpm workspaces, relative YAML paths and example output are resolved against the app root (detected via process.argv[1] heuristic), not process.cwd(). Override with outputDir if needed:

ConfigModule.forRoot({
  outputDir: 'apps/my-app',
  load: [appConfig],
})

License

MIT