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

@filipgorny/config

v0.0.1

Published

Type-safe configuration management library with built-in validation and dependency injection support

Downloads

10

Readme

@filipgorny/config

Type-safe configuration management library with built-in validation and dependency injection support.

Features

  • 🔒 Type-safe - Full TypeScript support with type inference
  • Automatic Validation - Validates required properties at runtime
  • 🎯 Simple API - Clean and intuitive interface
  • 🔄 Default Values - Built-in support for default configurations
  • 🪶 Lightweight - Minimal dependencies
  • 🔌 DI Support - Works seamlessly with dependency injection containers
  • 📄 DotEnv Integration - Built-in support for .env files
  • 🚀 Zero Config - Works out of the box

Installation

npm install @filipgorny/config

or

pnpm add @filipgorny/config

or

yarn add @filipgorny/config

Quick Start

import { createDotEnvConfig } from "@filipgorny/config";

const config = createDotEnvConfig();

// Read configuration with validation
const appConfig = config.read({
  PORT: 3000, // Default value
  NODE_ENV: "development", // Default value
  API_KEY: undefined, // Required (throws if missing)
  DATABASE_URL: undefined, // Required (throws if missing)
});

console.log(appConfig.PORT); // number
console.log(appConfig.API_KEY); // string
console.log(appConfig.DATABASE_URL); // string

Usage

Basic Configuration Reading

The read() method is the recommended way to read configuration values. It provides:

  • Automatic validation for required properties
  • Default values for optional properties
  • Type-safe return values
import { createDotEnvConfig } from "@filipgorny/config";

const config = createDotEnvConfig();

// Define your configuration schema
const appConfig = config.read({
  // Required properties (undefined = must exist)
  DATABASE_URL: undefined,
  API_KEY: undefined,

  // Optional properties with defaults
  PORT: 3000,
  NODE_ENV: "development",
  LOG_LEVEL: "info",
  ENABLE_DEBUG: false,
});

// All properties are now guaranteed to exist
console.log(appConfig.DATABASE_URL); // string - guaranteed to exist
console.log(appConfig.PORT); // number - from env or default
console.log(appConfig.NODE_ENV); // string - from env or default

Error Handling

When a required property is missing, a ConfigPropertyDoesNotExistError is thrown:

import {
  createDotEnvConfig,
  ConfigPropertyDoesNotExistError,
} from "@filipgorny/config";

const config = createDotEnvConfig();

try {
  const appConfig = config.read({
    REQUIRED_API_KEY: undefined,
  });
} catch (error) {
  if (error instanceof ConfigPropertyDoesNotExistError) {
    console.error(`Missing config: ${error.propertyName}`);
    // Output: Missing config: REQUIRED_API_KEY
    process.exit(1);
  }
}

Individual Property Access

You can also access properties individually using get() and has():

const config = createDotEnvConfig();

// Check if property exists
if (config.has("API_KEY")) {
  const apiKey = config.get<string>("API_KEY");
  console.log(apiKey);
}

// Get with type
const port = config.get<number>("PORT");
const debug = config.get<boolean>("DEBUG");

Custom .env File Path

By default, the library searches for .env file starting from the current directory and moving upward. You can specify a custom path:

import { createDotEnvConfig } from "@filipgorny/config";

// Absolute path
const config1 = createDotEnvConfig("/path/to/.env");

// Relative path
const config2 = createDotEnvConfig("./config/.env");

// Different environment files
const config3 = createDotEnvConfig(".env.production");

Organizing Configuration

Recommended Pattern:

Create a centralized configuration file for your application:

// src/config/index.ts
import { createDotEnvConfig } from "@filipgorny/config";

const configProvider = createDotEnvConfig();

export const config = configProvider.read({
  // Server
  PORT: 3000,
  HOST: "localhost",
  NODE_ENV: "development",

  // Database
  DATABASE_URL: undefined, // Required
  DATABASE_POOL_SIZE: 10,

  // External APIs
  OPENAI_API_KEY: undefined, // Required
  STRIPE_SECRET_KEY: undefined, // Required

  // Features
  ENABLE_CACHE: true,
  ENABLE_RATE_LIMITING: true,

  // Logging
  LOG_LEVEL: "info",
});

export type AppConfig = typeof config;

Then import it throughout your application:

// src/server.ts
import { config } from "./config";

const server = createServer({
  port: config.PORT,
  host: config.HOST,
});

// src/database.ts
import { config } from "./config";

const db = connectDatabase(config.DATABASE_URL);

API Reference

createDotEnvConfig(dotenvPath?: string): Config

Creates a Config instance with DotEnv provider.

Parameters:

  • dotenvPath (optional): Path to .env file. Defaults to .env and searches upward from current directory.

Returns: Config instance

Example:

const config = createDotEnvConfig();
const config = createDotEnvConfig(".env.production");
const config = createDotEnvConfig("/absolute/path/.env");

config.read<T>(properties: T): ConfigValues<T>

Reads multiple configuration properties with automatic validation.

Parameters:

  • properties: Object where keys are property names and values are default values
    • undefined = required property (throws if missing)
    • Any other value = default value if property doesn't exist

Returns: Object with all requested properties

Throws: ConfigPropertyDoesNotExistError if a required property is missing

Example:

const values = config.read({
  REQUIRED_PROP: undefined, // Must exist
  OPTIONAL_PROP: "default", // Uses default if missing
});

config.get<T>(key: string): T

Gets a single configuration value.

Parameters:

  • key: Configuration key name

Returns: Configuration value or undefined if not found

Example:

const apiKey = config.get<string>("API_KEY");
const port = config.get<number>("PORT");
const debug = config.get<boolean>("DEBUG");

config.has(key: string): boolean

Checks if a configuration key exists.

Parameters:

  • key: Configuration key name

Returns: true if key exists, false otherwise

Example:

if (config.has("OPTIONAL_FEATURE")) {
  enableFeature();
}

ConfigPropertyDoesNotExistError

Error thrown when a required configuration property is missing.

Properties:

  • propertyName: Name of the missing property
  • message: Error message
  • name: "ConfigPropertyDoesNotExistError"

Example:

try {
  config.read({ REQUIRED: undefined });
} catch (error) {
  if (error instanceof ConfigPropertyDoesNotExistError) {
    console.log(error.propertyName); // "REQUIRED"
  }
}

Advanced Usage

Custom Config Provider

You can create custom configuration providers by implementing the ConfigProvider interface:

import { Config, ConfigProvider } from "@filipgorny/config";

class CustomConfigProvider implements ConfigProvider {
  private data: Record<string, any> = {};

  constructor() {
    // Load config from your custom source
    this.data = loadFromCustomSource();
  }

  get(key: string): string | number | boolean | undefined {
    return this.data[key];
  }

  has(key: string): boolean {
    return key in this.data;
  }
}

const config = new Config(new CustomConfigProvider());

Dependency Injection Integration

The Config class works seamlessly with @filipgorny/di:

import { createContainer } from "@filipgorny/di";
import { DotEnvConfigProvider, Config } from "@filipgorny/config";

const container = createContainer();

// Register the provider
container.registerInstance("configProvider", new DotEnvConfigProvider());

// Register the config
container.register("config", Config);

// Use it
const config = container.get<Config>("config");

Type Definitions

For better type safety, define your config interface:

interface AppConfig {
  PORT: number;
  DATABASE_URL: string;
  API_KEY: string;
  NODE_ENV: "development" | "production" | "test";
  ENABLE_DEBUG: boolean;
}

const config = createDotEnvConfig();
const appConfig: AppConfig = config.read({
  PORT: 3000,
  DATABASE_URL: undefined,
  API_KEY: undefined,
  NODE_ENV: "development",
  ENABLE_DEBUG: false,
});

Best Practices

1. Centralize Configuration

Create a single configuration file that exports your app config:

// config/index.ts
export const config = createDotEnvConfig().read({
  // All your config here
});

2. Use Undefined for Required Properties

Always use undefined for properties that MUST exist:

const config = configProvider.read({
  CRITICAL_API_KEY: undefined, // ✅ Will throw if missing
  OPTIONAL_FEATURE: "off", // ✅ Has default
});

3. Validate Early

Load and validate configuration at application startup:

// index.ts
import { config } from "./config";

// Config is validated here, before starting the app
startApplication(config);

4. Use TypeScript

Define types for your configuration:

const config = configProvider.read({
  PORT: 3000,
  DEBUG: false,
});

type Config = typeof config;
// Config is: { PORT: number, DEBUG: boolean }

5. Environment-Specific Files

Use different .env files for different environments:

const envFile =
  process.env.NODE_ENV === "production"
    ? ".env.production"
    : ".env.development";

const config = createDotEnvConfig(envFile);

Environment Variables

The DotEnv provider automatically parses common types:

  • Strings: Any text value
  • Numbers: Automatically converted (e.g., PORT=30003000)
  • Booleans: "true" or "false" (case-insensitive) → true / false

Example .env file:

# Server
PORT=3000
HOST=localhost
NODE_ENV=development

# Database
DATABASE_URL=postgresql://localhost/mydb
DATABASE_POOL_SIZE=10

# API Keys
OPENAI_API_KEY=sk-xxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx

# Feature Flags
ENABLE_CACHE=true
ENABLE_RATE_LIMITING=false

# Logging
LOG_LEVEL=debug

Migration Guide

From Manual Validation

Before:

const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;

const apiKey = process.env.API_KEY;
if (!apiKey) {
  throw new Error("API_KEY is required");
}

const config = { port, apiKey };

After:

const config = createDotEnvConfig().read({
  PORT: 3000,
  API_KEY: undefined,
});

From dotenv Package

Before:

import dotenv from "dotenv";
dotenv.config();

const config = {
  port: parseInt(process.env.PORT || "3000"),
  apiKey: process.env.API_KEY!,
};

After:

import { createDotEnvConfig } from "@filipgorny/config";

const config = createDotEnvConfig().read({
  PORT: 3000,
  API_KEY: undefined,
});

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT

Author

Filip Gorny

Links