Askr
Guides

Environment Configuration

Use environment configuration with the current published Askr packages.

  • @askrjs/askr0.0.59

How to use environment configuration

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.

  1. Import the published @askrjs/askr entrypoint.
  2. Keep environment configuration configuration next to the component, route, or server composition root that owns it.
  3. Handle pending, unavailable, cancellation, and failure states, then verify the production path.
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.