Skip to main content

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_ENV is constrained at the boundary to `dev` | `staging` | `prod` | null instead of being normalized later from an arbitrary string.
  • Destructuring env inputs{ RANK_ENV } = Env<SysEnv> {} pulls the field you care about into a local binding while keeping the schema on the Env call.
  • match on a literal unionmatch RANK_ENV ?? dev`` branches over the closed set of allowed environments with an explicit default when the variable is unset.
  • Nested withbase with { db: base.db with { poolSize: 25 } } patches deeply without touching unrelated fields.
  • Multi-file modulesbase.rank exports the shared type and value; this example imports them via the canonical absolute form root::base. In a normal project source tree, a nearby sibling import can also use super::base when 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