Placeholders and .env
Fill config values from environment variables and .env files.
Syntax
A string value can refer to an environment variable:
| Placeholder | Value |
|---|---|
${VAR} | The value of VAR. A problem when VAR is not set |
${VAR:-default} | The value of VAR, or default when VAR is unset or empty |
${VAR:?message} | The value of VAR. A problem with message when VAR is unset or empty |
$${ | A literal ${ |
Placeholders are resolved in values only, never in keys.
http:
port: ${PORT:-3000}
database:
url: ${DATABASE_URL}
password: ${DATABASE_PASSWORD:?Set DATABASE_PASSWORD in .env.local}In TOML, JSONC and JSON5 a placeholder has to be a string, so quote it:
[server]
port = "${PORT:-3000}"Whole values are typed
A value that is exactly one placeholder has the same type for tsc and at run time:
- In the base file, it has the type of its default. With
PORT=8080,port: ${PORT:-3000}is the number8080. Without a default (${PORT}or${PORT:?message}), it is a string, soport: ${PORT}is'8080'. - In an environment or local file, it has the type of the base key it overrides. If
app.config.yamlhasport: 3000,port: ${PORT}inapp.production.config.yamlis the number8080.
The variable is read with the file's own rules for that type (true is a boolean in YAML and TOML), and a value that doesn't
read as that type is a problem. A placeholder inside a longer string stays a string: url: http://localhost:${PORT} is
'http://localhost:8080'.
| In the base file | Type | With PORT=8080, DEBUG=true, NAME=api |
|---|---|---|
port: ${PORT:-3000} | number | 8080 |
debug: ${DEBUG:-false} | boolean | true |
port: ${PORT} | string | '8080' |
name: ${NAME:?required} | string | 'api' |
url: http://localhost:${PORT} | string | 'http://localhost:8080' |
To get a number from a placeholder with no default, give it a default or validate it with a schema that coerces
(z.coerce.number(), see Schemas).
.env files
Placeholders read from these files, each later one winning over the ones before it:
.env.env.local.env.<environment>.env.<environment>.local
The real environment variables win over all of them. Each file is looked for next to the base config file first, then in the
project root (the nearest folder with a package.json). .env files are parsed with Node's util.parseEnv.
tyfigo never writes process.env. .env files only supply values to placeholders and to loadEnv; they are never imported as modules.
Bundled servers
A server bundle built with a bundler plugin reads .env files from the working directory when it starts, not from the
machine that built it. It picks its layers and .env.<env> files by NODE_ENV when it starts, unless the plugin's
environment option fixed them. See Server bundles, and
Browser builds for what client bundles do.
Problems
A missing variable stops the import with every problem listed:
Invalid config /srv/app/config/app.config.yaml
/srv/app/config/app.config.yaml:5:8 - Environment variable 'DATABASE_URL' is not set (missing-variable)A value that doesn't read as the placeholder's type is a placeholder-type problem, at the placeholder:
Invalid config /srv/app/config/app.config.yaml
/srv/app/config/app.production.config.yaml:2:9 - Expected number at 'http.port', but environment variable 'PORT' is "abc" (placeholder-type)A malformed placeholder such as ${1PORT} or an unclosed ${PORT is an invalid-placeholder problem, reported by tsc as
well as at run time.