Skip to content

Env composition

Build the app’s env schema from the blocks the mounted features register, instead of keeping a hand-written copy of every feature’s variables.

  • Feature blocks: createSecretsFeature, createCryptoShreddingFeature and createFileProviderS3Feature({ fromEnv: true }) register secretsEnvSchema, kmsEnvSchema and s3EnvSchema. composeEnvSchema collects them and records which feature declared each variable.
  • App-level blocks in extend: buildInfoEnvSchema, demoAdminEnvSchema, stripeEnvSchema({ prices }), paddleEnvSchema({ prices }) and createS3EnvSchema({ prefix }) for a second bucket. These belong to no mounted feature, so the app adds them.
  • Typed result: parseEnv(composed.schema, …) returns the merged type, so parsed.S3_BUCKET is string | undefined and parsed.APP_PUBLIC_URL is string without a cast.
  • Error report: a missing required variable is listed with its source (secrets, app, …).
BlockWhere it livesRegistered by
kmsEnvSchemacrypto-shreddingcreateCryptoShreddingFeature()
smtpEnvSchemachannel-emailcreateChannelEmailFeature()
s3EnvSchema, createS3EnvSchema({ prefix })file-provider-s3createFileProviderS3Feature({ fromEnv: true })
stripeEnvSchema({ prices })subscription-stripeapp (extend); the feature registers the bridge keys
paddleEnvSchema({ prices })subscription-paddleapp (extend); the feature registers the bridge keys
mfaTokenSecretsEnvSchemaauth-mfacreateAuthMfaFeature()
buildInfoEnvSchema, demoAdminEnvSchemafoundation-sharedapp (extend)
prometheusMetricsEnvSchemaframework/observabilityapp (extend)

Every app that boots with runProdApp. Pass the composed schema as envSchema and read the parsed values instead of process.env.


📄 On GitHub: samples/recipes/env-composition