config-kit overview
config-kit loads configuration from one or more sources, merges them in order, validates the result against a schema, and gives you back a typed object. Wrong env var? Build fails at boot, not at request time.
The shape of a load
Section titled “The shape of a load”import { z } from "zod";import { loadConfig, dotenvFileSource, processEnvSource,} from "@arshad-shah/config-kit";
const config = await loadConfig({ schema: z.object({ NODE_ENV: z.enum(["development", "production", "test"]).default("development"), PORT: z.coerce.number().int().positive().default(3000), DATABASE_URL: z.string().url(), LOG_LEVEL: z.enum(["trace", "debug", "info", "warn", "error"]).default("info"), }), sources: [ dotenvFileSource(".env"), dotenvFileSource(".env.local"), processEnvSource(), ],});
config.PORT; // numberconfig.DATABASE_URL; // string, validated as URLconfig is fully typed via z.infer<typeof schema>. There’s no shadow type definition to keep in sync.
What you get
Section titled “What you get”- Schema-agnostic - works with Zod, Valibot, ArkType, or anything with a
parse(input) → outputmethod - Flat or structured sources - flat env maps and nested, module-based config files (
app.config.ts); see Module-based config - Layered sources - load from multiple places; later sources override earlier ones (flat sources merge key-by-key, structured sources deep-merge)
- Soft source failures - a missing
.env, config file, or unreachable remote endpoint doesn’t crash the load - Strict or warn - throw on invalid config (default) or downgrade to a logged warning with
mode: "warn" - Host-controlled errors -
onValidationErrorhands you the raw validation error (e.g. aZodError) to render your own message - Secret-safe errors - validation errors redact quoted values by default so secrets don’t end up in logs
- Optional logger - pass any object with
info/warn/errormethods (compatible with log-kit) for source-load diagnostics
What it doesn’t do
Section titled “What it doesn’t do”- No automatic file-watching/reload (config is captured at boot; for hot reload, build it yourself)
- No built-in TypeScript compiler for config files -
configFileSourcetakes aloadhook so you bring esbuild/jiti (or rely on native import for.js/.mjs/.cjs/.json) - No defaults beyond
objectSourceand your schema’s defaults (Zod has.default(), Valibot hasoptional()with a fallback)
Composition with log-kit
Section titled “Composition with log-kit”If you have a logger, pass it in:
import { createLogger } from "@arshad-shah/log-kit";
const log = createLogger();const config = await loadConfig({ schema, sources, logger: log });The logger gets info for each successful source load and warn if a source throws. No hard coupling - any object with the right shape works.