Environment Configuration
Environment Configuration: a worked guide from route registry through to a production build.
Example
Read and validate environment values once in the server composition root; pass a typed config object to services instead of reading globals throughout the application.
export const config = Object.freeze({
apiUrl: requireUrl(process.env.API_URL, 'API_URL'),
oidcIssuer: requireUrl(process.env.OIDC_ISSUER, 'OIDC_ISSUER'),
port: requireInteger(process.env.PORT ?? '3000', 'PORT'),
});
const app = createApplication({ config });Goal and architecture
Askr splits configuration along the same boundary as the rest of the framework: build-time and browser config comes from Vite's import.meta.env, and server process configuration is plain process.env read where you construct your server app. There's no Askr-specific config-loading module — listen(app, { port, host, backlog, signal }) and serve(app, { assets, signals }) from @askrjs/node take their settings as plain options objects, and you're expected to populate those options from process.env yourself.
Implementation
Read server settings once at startup — const port = Number(process.env.PORT) || 3000 — and pass them into listen(app, { port, host }) or serve(app, { port, assets: { root } }) rather than reading process.env scattered through route handlers. Client-exposed values must go through Vite's VITE_-prefixed import.meta.env variables so the bundler statically replaces them at build time; anything without that prefix stays server-only and never reaches the browser bundle, which is the actual security boundary here, not a convention you have to enforce by hand.
Failure states
Fail fast on missing required configuration — validate process.env at startup (a required DATABASE_URL or JWKS URL that's absent should throw before listen() is called, not produce a cryptic runtime error on the first request). A misconfigured VITE_API_BASE_URL that's silently undefined will make every @askrjs/fetch client call fail with a network error that looks unrelated to configuration, so validate build-time env vars in your Vite config rather than discovering the problem in production logs.
Verification
Test config parsing as a pure function — given a process.env-shaped object, assert it produces the expected typed config or throws for each missing/invalid required key. Confirm your .env files for different environments (development, staging, production) don't leak server-only secrets into VITE_-prefixed names, since anything with that prefix ships to the browser. Run a build with a deliberately incomplete environment in CI to catch missing required variables before they reach a deploy.