@mantlejs/config
Environment-aware configuration management for Mantle JS. Loads JSON config files from a config/ directory, merges environment-specific overrides, applies MANTLE_* env var overrides, and optionally validates the result against a TypeBox schema at startup.
Installation
Section titled “Installation”npm install @mantlejs/configFor schema validation, also install the peer dependency:
npm install @sinclair/typeboxConcepts
Section titled “Concepts”File loading order
Section titled “File loading order”Config is assembled by merging three sources in order — later values override earlier ones:
config/default.json— base config present in all environmentsconfig/{NODE_ENV}.json— environment-specific overrides (e.g.config/production.json). IfNODE_ENV(or a customenvVar) is unset, this defaults to"development", soconfig/development.jsonis the overlay loaded by default.MANTLE_*environment variables — highest priority, applied last
Environment variable overrides
Section titled “Environment variable overrides”Any env var prefixed with MANTLE_ (configurable via envPrefix) maps to a config key. Double underscore (__) navigates nested objects:
MANTLE_PORT=8080 → config.port = 8080MANTLE_DB__POOL__MAX=25 → config.db.pool.max = 25Use envPrefix to namespace overrides to your own app name:
MYAPP_PORT=8080 → config.port = 8080MYAPP_DB__POOL__MAX=25 → config.db.pool.max = 25Values are coerced to number or boolean when the base config value has that type.
Schema validation
Section titled “Schema validation”Pass a TypeBox schema via options.schema and the plugin validates the merged config at startup. A mismatch throws GeneralError('Invalid configuration', { errors }) before the server accepts any requests — fail fast, fail loud.
Quick start
Section titled “Quick start”config/ default.json production.json{ "port": 3030, "db": { "client": "pg", "pool": { "min": 2, "max": 10 } }}
// config/production.json{ "port": 8080, "db": { "pool": { "max": 25 } }}import { mantle } from "@mantlejs/mantle";import { express } from "@mantlejs/express";import { config } from "@mantlejs/config";import { Type } from "@sinclair/typebox";
const AppConfigSchema = Type.Object({ port: Type.Number(), db: Type.Object({ client: Type.String(), pool: Type.Object({ min: Type.Number(), max: Type.Number() }), }),});
const app = mantle() .configure(express()) .configure(config({ schema: AppConfigSchema }));
// Read the whole merged configconst cfg = app.get<{ port: number; db: { client: string } }>("config");
// Top-level keys are also set individually for convenienceconst port = app.get<number>("port"); // 3030 (or 8080 in production)config(options?)
Section titled “config(options?)”Returns a MantlePlugin. Call via app.configure(config(options)).
import { config } from "@mantlejs/config";
app.configure( config({ directory: "config", // optional — default: process.cwd() + '/config' schema: AppConfigSchema, // optional — TypeBox schema for startup validation envVar: "NODE_ENV", // optional — env var that selects the overlay file envPrefix: "MYAPP_", // optional — default: 'MANTLE_' }),);Side effects:
- Sets
app.set('config', mergedConfig)— the full merged object - Sets
app.set(key, value)for each top-level key in the merged config
ConfigOptions
Section titled “ConfigOptions”| Field | Type | Default | Description |
|---|---|---|---|
directory |
string |
process.cwd() + '/config' |
Directory containing config JSON files |
schema |
TSchema |
— | TypeBox schema; throws GeneralError if merged config is invalid |
envVar |
string |
"NODE_ENV" |
Env var used to select the environment overlay file |
envPrefix |
string |
"MANTLE_" |
Prefix for env var overrides — use your app name to avoid collisions |
Accessing configuration
Section titled “Accessing configuration”// Full merged config objectconst cfg = app.get<AppConfig>("config");
// Individual top-level keys (set as a convenience by the plugin)const port = app.get<number>("port");const db = app.get<AppConfig["db"]>("db"); // the whole "db" object — only top-level keys are set this way
// Nested values require reading the full configconst maxPool = app.get<AppConfig>("config").db.pool.max;Environment variable overrides
Section titled “Environment variable overrides”| Env var | Maps to |
|---|---|
MANTLE_PORT=8080 |
config.port = 8080 |
MANTLE_DB__CLIENT=mysql2 |
config.db.client = "mysql2" |
MANTLE_DB__POOL__MAX=25 |
config.db.pool.max = 25 |
With envPrefix: "MYAPP_":
| Env var | Maps to |
|---|---|
MYAPP_PORT=8080 |
config.port = 8080 |
MYAPP_DB__POOL__MAX=25 |
config.db.pool.max = 25 |
Key segments are lowercased. Values are coerced:
- If the existing value is a
number, the env string is passed throughNumber(). - If the existing value is a
boolean,"true"and"1"becometrue; everything else becomesfalse. - Otherwise the raw string is used.
import type { ConfigOptions } from "@mantlejs/config";| Type | Description |
|---|---|
ConfigOptions |
Options passed to config() |
Development
Section titled “Development”npx nx build config # compilenpx nx test config # run testsnpx nx lint config # lintPublishing
Section titled “Publishing”Build before publishing:
npx nx build configFirst publish (scoped packages require --access public):
cd packages/confignpm publish --access publicSubsequent releases — bump version in packages/config/package.json, then:
cd packages/confignpm publish