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

@devbro/neko-config

v0.1.17

Published

Structured configuration loading and management for TypeScript apps - tame your config files like a pro

Readme

@devbro/neko-config

A lightweight, type-safe configuration management library with TypeScript support. Part of the Neko and Pashmak ecosystem. tested with Nodejs and Bun.

Installation

npm install @devbro/neko-config

Features

  • 🎯 Simple API - Easy to use with intuitive methods
  • 🔒 Type-Safe - Full TypeScript support with autocomplete
  • 🌲 Nested Access - Access deeply nested values using dot notation
  • 🔄 Multiple Instances - Support for multiple configuration instances
  • 📦 Singleton Pattern - Default singleton instance for convenience
  • 🎨 Flexible - Works with any JSON-like configuration structure

Quick Start

Loading Configuration

First, load your configuration data in your application entry point:

// loader.ts or app.ts
import { config } from '@devbro/neko-config';

const configData = {
  app: {
    name: 'MyApp',
    port: 3000,
    debug: true,
  },
  database: {
    primary: {
      type: 'postgesql',
      host: 'localhost',
      port: 5432,
      uri: 'postgresql://localhost:5432/mydb',
    },
  },
  cache: {
    redis: [
      { uri: 'redis://localhost:6379/0' },
      { uri: 'redis://localhost:6379/1' },
      { uri: 'redis://localhost:6379/2' },
    ],
  },
  service_is_active: () => { return true; },
};

config.load(configData);

Basic Usage

Once loaded, you can access configuration values anywhere in your application:

import { config } from '@devbro/neko-config';

// Get a value with a default fallback
const port = config.get('app.port', 3000);

// Get a nested value
const dbHost = config.get('database.primary.host');

// Access array elements
const redisUri = config.get('cache.redis[2].uri');

// Check if a key exists
if (config.has('app.debug')) {
  console.log('Debug mode is configured');
}

// Get the entire configuration object
const allConfig = config.all();

Advanced Features

Typed Configuration Keys

For enhanced type safety and autocomplete, you can define your configuration schema using TypeScript module augmentation. This is especially useful in larger projects where you want to catch configuration errors at compile time.

Step 1: Define Your Configuration Types

Create a type declaration file (e.g., types/config.d.ts or src/config.d.ts):

// types/config.d.ts
declare module '@devbro/neko-config' {
  interface ConfigKeys {
    'app.name': string;
    'app.port': number;
    'app.debug': boolean;
    'database.host': string;
    'database.port': number;
    'database.username': string;
    'database.password': string;
    'cache.redis[0].uri': string;
    'api.baseUrl': string;
    'api.timeout': number;
  }
}

//infer type from existing json object
import { DotPathRecord } from "@devbro/pashmak/config";

declare module "@devbro/neko-config" {
  interface ConfigKeys extends DotPathRecord<typeof project_configs> {}
}

Step 2: Enjoy Type Safety

After defining your typed keys, TypeScript will provide full autocomplete and type checking:

import { config } from '@devbro/neko-config';

// ✅ TypeScript knows this is a string - full autocomplete!
const appName = config.get('app.name');

// ✅ TypeScript knows this is a number
const port = config.get('app.port');

// ✅ Type inference works with default values
const timeout = config.get('api.timeout', 5000); // number

// ❌ TypeScript will show an error for undefined keys
// const invalid = config.get('$.app.nonexistent'); // Error!

// 💡 You can still use dynamic keys when needed
const dynamicKey = config.get('some.dynamic.path' as any);

Benefits of Typed Configuration:

  • Autocomplete: Your IDE will suggest available configuration keys
  • Type Safety: Catch typos and missing keys at compile time
  • Documentation: Configuration schema serves as self-documenting code
  • Refactoring: Safely rename or restructure configuration keys

Note: If you don't augment the ConfigKeys interface, the library accepts any string key (default behavior), providing maximum flexibility.

Functional Configs

There may be situations that config values needs to be calculated using a function. For example, you are loading values from Launch Darkly or values are reloaded periodically from Secret Manager. In these cases you can use a function that is evaluated at get() .

let project_values = {
  f1: () => { ???? return "hello world!"; }
}
config.load(project_values);

console.log(config.get('f1'));

There are a few details and limitations that need to be kept in mind:

  • functional configs cannot be async, if your config is returning a promise, then you need to await config.get('f1');
  • functional configs cannot recieve arguments.
  • functional configs are called on each get()

Multiple Configuration Instances

While the singleton config instance is convenient for most use cases, you can create multiple independent configuration instances when needed. This is useful for:

  • Testing with different configurations
  • Multi-tenant applications
  • Isolating configuration scopes
import { Config } from '@devbro/neko-config';

// Create separate configuration instances
const appConfig = new Config();
appConfig.load({
  environment: 'production',
  features: { newUI: true },
});

const testConfig = new Config();
testConfig.load({
  environment: 'test',
  features: { newUI: false },
});

// Each instance maintains its own state
console.log(appConfig.get('environment')); // 'production'
console.log(testConfig.get('environment')); // 'test'

NODE_ENV and environment specific configs

We can use loadConfig to load files that contain environment specific configs:

//logger.ts
import { ctxSafe } from "@devbro/pashmak/context";
import { LogMessage } from "@devbro/pashmak/logger";

export default {
  level: "info",
  extrasFunction: (message: LogMessage) => {
    let requestId = ctxSafe()?.get("requestId");
    requestId && (message.requestId = requestId);
    return message;
  },
};

export const $test = {
  level: "silent",
}

//default.ts
let project_configs = {
  ...
  loggers: await loadConfig("./loggers"),
  ...
}

If value of a config is a Promise it will be resolved at load() so the value is calculated only once.

API Reference

Config Class

Methods

load(data: object): void

Load configuration data into the instance.

config.load({ app: { name: 'MyApp' } });
get(key: string, defaultValue?: T): T

Retrieve a configuration value by key. Returns the default value if the key doesn't exist.

const port = config.get('app.port', 3000);
const name = config.get<string>('app.name');
has(key: string): boolean

Check if a configuration key exists.

if (config.has('database.uri')) {
  // Configuration exists
}
all(): object

Get the entire configuration object.

const allConfig = config.all();

Dot Notation

The library supports flexible dot notation for accessing nested values:

// Object properties
config.get('database.primary.host');

// Array elements
config.get('servers[0].url');

// Mixed
config.get('services.api.endpoints[0].path');

Best Practices

  1. Load Early: Load your configuration as early as possible in your application lifecycle
  2. Use Types: Define typed configuration keys for better developer experience
  3. Environment Variables: Combine with environment variables for different deployment environments
  4. Default Values: Always provide sensible defaults when calling get()
  5. Validation: Validate critical configuration values after loading

Example: Complete Setup

// config/index.ts
import { config } from '@devbro/neko-config';
import fs from 'fs';
import path from 'path';

// Load configuration from file
const configPath = path.join(__dirname, 'config.json');
const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));

// Merge with environment variables
const finalConfig = {
  ...configData,
  app: {
    ...configData.app,
    port: process.env.PORT || configData.app.port,
    env: process.env.NODE_ENV || 'development',
  },
};

config.load(finalConfig);

// Export for use in other modules
export { config };
// app.ts
import { config } from './config';

const port = config.get('app.port', 3000);
const appName = config.get('app.name');

console.log(`Starting ${appName} on port ${port}`);

License

MIT

Contributing

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