A type-safe configuration management library for TypeScript applications.
- Full Type Safety: Autocomplete for section and element names, with types inferred from your schema
- Easy Validation: Validate all config at startup with
validate()or get detailed error reports - Schema-Based: Define your configuration declaratively with Zod validators
- Flexible Binds: Environment variables, files, or custom sources
- Documentation Export: Generate JSON or YAML documentation from your config schema
npm install config-boundimport {
ConfigBound,
configItem,
configEnum,
configSection
} from '@config-bound/core';
import { EnvVarBind } from '@config-bound/core/binds/env';
import { z } from 'zod';
// Define your configuration schema with full type safety
const config = ConfigBound.createConfig(
{
port: configItem<number>({
default: 3000,
validator: z.number().int().min(0).max(65535),
description: 'Application port'
}),
environment: configEnum({
values: ['development', 'production'],
default: 'development',
description: 'Runtime environment'
}),
database: configSection(
{
host: configItem<string>({
default: 'localhost',
validator: z.string(),
description: 'Database host'
}),
port: configItem<number>({
default: 5432,
validator: z.number().int().min(0).max(65535),
description: 'Database port'
})
},
'Database configuration'
)
},
{
binds: [await EnvVarBind.create({ prefix: 'MYAPP' })],
validateOnInit: true // Catch config errors at startup!
}
);
// Access values with full type safety - types are inferred, autocomplete works!
const port = config.get('app', 'port'); // TypeScript knows this is number
const env = config.get('app', 'environment'); // TypeScript knows this is "development" | "production"
const dbHost = config.get('database', 'host'); // TypeScript knows this is string
// Validate all config at once
try {
config.validate();
console.log('✅ All configuration is valid');
} catch (error) {
console.error('❌ Configuration errors:', error);
}For advanced use cases where you need fine-grained control over construction, you can use the imperative API:
import { ConfigBound } from '@config-bound/core';
import { Section } from '@config-bound/core/section/section';
import { Element } from '@config-bound/core/element';
import { EnvVarBind } from '@config-bound/core/binds/env';
// Create configuration elements
const portElement = new Element<number>('port', 'Application port', 3000);
const logLevelElement = new Element<string>(
'logLevel',
'Logging level',
'info'
);
// Create a configuration section
const appSection = new Section('app', [portElement, logLevelElement]);
// Create the config instance
const config = new ConfigBound(
'app',
[await EnvVarBind.create({ prefix: 'MYAPP' })],
[appSection]
);
// Use it in your application (still fully type-safe!)
const port = config.get('app', 'port');Note: The declarative createConfig API is recommended for most use cases.
ConfigBound provides schema export functionality through the @config-bound/schema-export package:
npm install @config-bound/schema-exportimport {
exportSchema,
formatAsJSON,
formatAsYAML
} from '@config-bound/schema-export';
// Get structured schema object
const schema = exportSchema(config.name, config.sections);
// Export as JSON
const json = formatAsJSON(schema);
// Export as YAML
const yaml = formatAsYAML(schema);This is useful for:
- Generating documentation automatically
- Creating IDE autocomplete schemas
- Validating environment variables
- Building configuration UIs
- API documentation
See the Export Documentation for detailed usage and examples.
ConfigBound automatically maps configuration to environment variables using the EnvVarBind. By default, it uses a prefix to avoid conflicts:
const config = ConfigBound.createConfig(
{
/* your schema */
},
{
binds: [await EnvVarBind.create({ prefix: 'MYAPP' })]
}
);This creates environment variables like:
MYAPP_APP_PORTforapp.portMYAPP_DATABASE_HOSTfordatabase.hostMYAPP_API_APIKEYforapi.apiKey
Override values at runtime:
MYAPP_APP_PORT=8080
MYAPP_DATABASE_HOST=prod-db.example.com
MYAPP_API_APIKEY=your-secret-keyUse StaticBind to inject values directly in code while still participating in bind priority order.
import { ConfigBound, configItem } from '@config-bound/core';
import { EnvVarBind } from '@config-bound/core/binds/env';
import { StaticBind } from '@config-bound/core/binds/static';
import { z } from 'zod';
const config = ConfigBound.createConfig(
{
port: configItem<number>({
default: 3000,
validator: z.number().int().min(0).max(65535)
})
},
{
// Earlier bind wins: StaticBind overrides EnvVarBind here.
binds: [
await StaticBind.create({ 'app.port': 8080 }),
await EnvVarBind.create({ prefix: 'MYAPP' })
]
}
);StaticBind accepts either nested values ({ app: { port: 8080 } }) or flat dot-path keys ({ 'app.port': 8080 }).
See the documentation site.
See the examples.
This project uses Turbo for efficient task orchestration across workspaces.
# Build main package only
npm run build
# Build all packages (including examples)
npm run build:all
# Start development with watch mode
npm run dev
# Run all tests
npm run test
# Format all code
npm run format
# Lint all code
npm run lint
# Clean build artifacts
npm run clean
# Run CI checks (format, lint, test)
npm run check
# Run examples
npm run examples
# Run specific example
npm run start:envVarExample- Caching: Turbo caches successful builds and tests for faster subsequent runs
- Parallelization: Tasks run in parallel where possible
- Dependency Management: Automatically builds dependencies before dependent tasks
We welcome contributions! Please see CONTRIBUTING.md for details.
MIT License - see LICENSE for details.