Multi-env App
Read RANK_ENV from the environment and produce an environment-specific configuration by patching a shared base config.
Source
examples/multi-env-app/base.rank
/// Shared base configuration for all environments.
pub AppBase = Object {
name: string,
port: number,
replicas: number,
logLevel: string,
db: Object {
host: string,
port: number,
name: string,
poolSize: number,
},
}
pub base: AppBase = {
name: `my-app`,
port: 8080,
replicas: 1,
logLevel: `info`,
db: {
host: `localhost`,
port: 5432,
name: `app`,
poolSize: 5,
},
}
examples/multi-env-app/main.rank
/// Reads RANK_ENV from the environment and produces an environment-specific
/// config by patching the shared base with per-environment overrides.
/// Shows Env inputs, match on a union type, and the `with` override pattern.
use std::Env
use root::base::{ base, AppBase }
SysEnv = Object {
RANK_ENV?: `dev` | `staging` | `prod` | null,
...: string,
}
{ RANK_ENV } = Env<SysEnv> {}
config: AppBase = match RANK_ENV ?? `dev` {
`dev` => base with {
logLevel: `debug`,
db: base.db with { poolSize: 2 },
},
`staging` => base with {
replicas: 2,
db: base.db with {
host: `db.staging.internal`,
poolSize: 10,
},
},
`prod` => base with {
replicas: 5,
logLevel: `warn`,
db: base.db with {
host: `db.prod.internal`,
poolSize: 25,
},
},
}
pub main = || config
Output
// RANK_ENV=prod
{
"name": "my-app",
"port": 8080,
"replicas": 5,
"logLevel": "warn",
"db": {
"host": "db.prod.internal",
"port": 5432,
"name": "app",
"poolSize": 25
}
}
Key concepts
std::Env— reads environment variables into a typed schema.RANK_ENVis constrained at the boundary to`dev` | `staging` | `prod` | nullinstead of being normalized later from an arbitrarystring.- Destructuring env inputs —
{ RANK_ENV } = Env<SysEnv> {}pulls the field you care about into a local binding while keeping the schema on theEnvcall. matchon a literal union —match RANK_ENV ??dev`` branches over the closed set of allowed environments with an explicit default when the variable is unset.- Nested
with—base with { db: base.db with { poolSize: 25 } }patches deeply without touching unrelated fields. - Multi-file modules —
base.rankexports the shared type and value; this example imports them via the canonical absolute formroot::base. In a normal project source tree, a nearby sibling import can also usesuper::basewhen that reads better.
Run it
RANK_ENV=prod rank examples/multi-env-app
RANK_ENV=staging rank examples/multi-env-app
rank examples/multi-env-app # defaults to dev