Skip to content

Sources

Sources define where the library reads your configuration from, such as files or environment variables.

By default, if no parser is specified, @layerfig/config uses its internal JSON parser for .json files.

export const config = new ConfigBuilder({
validate: (finalConfig) => schema.parse(finalConfig)
})
.addSource("base.json")
.addSource("prod.json")
.build();

To support various project needs, we provide parsers for other common configuration file formats:

… or create your own parser.

Consider the following scenario:

Your application’s configuration files are committed to version control and used to build a Docker image for production. When running this image locally for debugging, any change to a configuration value requires rebuilding the image. This process can be slow and results in testing a modified image rather than the actual production build.

To avoid this, use ConfigBuilder.createEnvVarSource() to override configuration values at runtime without modifying the source files:

import { ConfigBuilder } from "@layerfig/config";
import { schema } from "./schema";
export const config = new ConfigBuilder({
validate: (finalConfig) => schema.parse(finalConfig)
})
.addSource("base.json")
.addSource(ConfigBuilder.createEnvVarSource())
.build();

By default, the library expects environment variables with the following structure:

  • Prefix: "APP"
  • Prefix separator: "_"
  • Nested key separator: "__"

For example, the environment variable APP_port overrides the port key in your configuration.

Suppose your base.json contains:

{
"appURL": "http://localhost:4444",
"port": 4444,
"appEnv": "local"
}

Define your schema and load the sources:

export const config = new ConfigBuilder({
validate: (finalConfig, z) => {
const schema = z.object({
appURL: z.url(),
port: z.coerce.number().int().positive(),
appEnv: z.enum(["local", "dev", "prod"])
});
return schema.parse(finalConfig)
}
})
.addSource("base.json")
.addSource(ConfigBuilder.createEnvVarSource())
.build();

Run the application with overrides:

Terminal window
APP_appEnv=prod node index.js

Since the environment variable source is added last, its values take precedence.

Use __ (double underscores) as a separator to target nested keys:

const AppConfigSchema = z.object({
api: z.object({
vendor: z.object({
aws: z.object({
apiToken: z.string(),
}),
}),
}),
});
const config = new ConfigBuilder(AppConfigSchema)
.addSource("base.json")
.addSource(ConfigBuilder.createEnvVarSource())
.build();
Terminal window
APP_api__vendor__aws__apiToken=12345 node index.js

You can customize the prefix, prefix separator, and nested-key separator:

const config = new ConfigBuilder(AppConfigSchema)
.addSource("base.json")
.addSource(
ConfigBuilder.createEnvVarSource({
prefix: "VULCAN",
prefixSeparator: "--",
separator: "----",
})
)
.build();
Terminal window
VULCAN--api----vendor----aws----apiToken=12345 node index.js