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

favour-config-loader

v1.0.0

Published

Type-safe configuration loader for backend systems.

Readme

Config Loader

A type-safe configuration loader for Node.js backend systems. Load, validate, and type-check environment variables at runtime with full TypeScript support.

Why Config Loader?

Environment variables are strings. Without validation, you risk crashes at runtime:

// Without validation
const port = process.env.PORT; // Could be "abc", could be empty
app.listen(port); // Crashes if invalid

// With Config Loader
const config = loadConfig(schema); // Validates immediately
if (config.status === "success") {
  app.listen(config.data.port); // Safe, type-checked
}

Config Loader catches configuration errors before they reach production.

Installation

npm install @favour/config-loader

Quick Start

1. Define Your Configuration Type

type AppConfig = {
  port: number;
  databaseUrl: string;
  apiKey: string;
  environment: "production" | "development";
};

2. Create a Schema

import { Schema } from "@favour/config-loader";

const appConfigSchema: Schema<AppConfig> = {
  port: {
    type: "number",
    required: true,
    validate: (value) => value > 0 && value < 65536,
  },
  databaseUrl: {
    type: "string",
    required: true,
    validate: (value) => value.includes("://"),
  },
  apiKey: {
    type: "string",
    required: true,
    validate: (value) => value.length >= 10,
  },
  environment: {
    type: "string",
    required: true,
  },
};

3. Load Configuration

import { loadConfig } from "@favour/config-loader";

const result = loadConfig(appConfigSchema);

if (result.status === "success") {
  console.log("Port:", result.data.port);
  console.log("Database:", result.data.databaseUrl);
} else {
  console.error("Config error:", result.message);
  process.exit(1);
}

4. Set Environment Variables

Create a .env file:

PORT=3000
DATABASE_URL=postgresql://localhost:5432/mydb
API_KEY=your_secret_key_here
ENVIRONMENT=development

Load it with dotenv:

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

const config = loadConfig(appConfigSchema);

API Reference

Schema Type

type Schema<T> = {
  [K in keyof T]: {
    type: "string" | "number" | "boolean";
    required: boolean;
    validate?: (value: T[K]) => boolean;
  };
};

Each field in your schema defines:

  • type: The expected data type ("string", "number", "boolean")
  • required: Whether the field must be present
  • validate: Optional validation function that returns true if valid

loadConfig Function

function loadConfig<T>(schema: Schema<T>): Result<T>;

Loads environment variables and validates them against the schema.

Returns: A Result<T> which is either:

  • { status: "success"; data: T } - Configuration loaded and valid
  • { status: "error"; message: string } - Configuration invalid or missing

Result Type

type Result<T> =
  | { status: "success"; data: T }
  | { status: "error"; message: string };

validateConfig Function

function validateConfig<T>(config: T, schema: Schema<T>): T;

Manually validate a configuration object against a schema. Throws an error if validation fails.

Usage Examples

Basic Configuration

import { loadConfig, Schema } from "@favour/config-loader";

type Config = {
  appName: string;
  port: number;
};

const schema: Schema<Config> = {
  appName: {
    type: "string",
    required: true,
  },
  port: {
    type: "number",
    required: true,
    validate: (value) => value > 1024,
  },
};

const result = loadConfig(schema);

if (result.status === "success") {
  console.log(`${result.data.appName} running on port ${result.data.port}`);
}

With Optional Fields

type Config = {
  requiredField: string;
  optionalField: string;
};

const schema: Schema<Config> = {
  requiredField: {
    type: "string",
    required: true,
  },
  optionalField: {
    type: "string",
    required: false,
  },
};

Multiple Validators

const schema: Schema<AppConfig> = {
  apiKey: {
    type: "string",
    required: true,
    validate: (value) => {
      // Multiple checks
      return value.length >= 10 && value.includes("-");
    },
  },
};

Database Configuration

type DatabaseConfig = {
  host: string;
  port: number;
  username: string;
  password: string;
  database: string;
};

const schema: Schema<DatabaseConfig> = {
  host: {
    type: "string",
    required: true,
    validate: (value) => value.length > 0,
  },
  port: {
    type: "number",
    required: true,
    validate: (value) => value > 0 && value < 65536,
  },
  username: {
    type: "string",
    required: true,
  },
  password: {
    type: "string",
    required: true,
    validate: (value) => value.length >= 8,
  },
  database: {
    type: "string",
    required: true,
  },
};

Error Handling

Config Loader never crashes silently. It returns errors explicitly:

const result = loadConfig(schema);

if (result.status === "error") {
  // Handle the error
  console.error("Configuration failed:", result.message);

  // Exit process
  process.exit(1);
}

// Type-safe access to data
const config = result.data; // TypeScript knows this is your config type

How Config Loader Works

  1. Reads environment variables from process.env
  2. Converts variable names (camelCase to UPPER_SNAKE_CASE):
    • databaseUrlDATABASE_URL
    • apiKeyAPI_KEY
    • portPORT
  3. Converts values to correct types (strings to numbers, etc.)
  4. Runs validation functions
  5. Returns result or error

Testing

Run tests:

npm test

Watch mode (re-run on file changes):

npm run test:watch

Real-World Patterns

Pattern 1: Separate Config by Environment

// config/app.ts
const appConfigSchema: Schema<AppConfig> = { ... };

// config/database.ts
const dbConfigSchema: Schema<DatabaseConfig> = { ... };

// config/index.ts
export function initializeConfig() {
  const appConfig = loadConfig(appConfigSchema);
  const dbConfig = loadConfig(dbConfigSchema);

  if (appConfig.status === "error" || dbConfig.status === "error") {
    throw new Error("Configuration failed");
  }

  return {
    app: appConfig.data,
    db: dbConfig.data,
  };
}

Pattern 2: Default Values

const schema: Schema<Config> = {
  environment: {
    type: "string",
    required: false, // Optional
  },
};

const result = loadConfig(schema);
const environment =
  result.status === "success" ? result.data.environment : "development"; // Default

Pattern 3: Nested Configuration

For complex configurations, create separate types and loaders:

type ServerConfig = {
  port: number;
  host: string;
};

type AuthConfig = {
  jwtSecret: string;
  tokenExpiry: number;
};

type FullConfig = {
  server: ServerConfig;
  auth: AuthConfig;
};

// Load separately then combine
const serverResult = loadConfig(serverSchema);
const authResult = loadConfig(authSchema);

Best Practices

  1. Define schemas close to where they're used
  2. Validate early, at application startup
  3. Use specific validation rules (length, format, range)
  4. Fail fast - exit if configuration is invalid
  5. Document required environment variables in .env.example
  6. Never log sensitive values (passwords, API keys)

Environment Variables Example

Create .env.example for documentation:

# Server Configuration
PORT=3000
ENVIRONMENT=development

# Database
DATABASE_URL=postgresql://localhost:5432/mydb

# Security
API_KEY=your_api_key_here
JWT_SECRET=your_jwt_secret_here

Then create .env for actual values and add .env to .gitignore.

License

MIT

Contributing

Contributions welcome. Please write tests for new features.

npm test

Troubleshooting

"Is required" error

Make sure the environment variable is set:

export DATABASE_URL=postgresql://...
npm start

Or in .env:

DATABASE_URL=postgresql://...

"Validation failed" error

Check your validation function. For example, if port validation is:

validate: (value) => value > 0;

Make sure the value is actually greater than 0. Add logging to debug:

validate: (value) => {
  console.log("Validating port:", value);
  return value > 0;
};

Type errors in TypeScript

Make sure your config type matches your schema:

type Config = {
  port: number;
};

// Schema must have all fields from Config
const schema: Schema<Config> = {
  port: { ... }
  // Missing: other fields?
};