Environment Validation
Standard Schema validation for server and browser environment values
Bunderstack validates environment values before the application starts.
Server-only and public values stay separate, while app.env and procedure
contexts remain fully typed.
Configure schemas
Each entry accepts Standard Schema. This example uses Valibot:
import * as v from 'valibot'
export const env = {
server: {
STRIPE_API_KEY: v.string(),
WEBHOOK_SECRET: v.string(),
},
client: {
PUBLIC_APP_URL: v.pipe(v.string(), v.url()),
PUBLIC_SENTRY_DSN: v.optional(v.string()),
},
}
export const backend = bunderstack({ schema, database, env })
export const app = await backend.start()Server keys must not start with PUBLIC_; browser-safe keys must. Naming
violations and invalid values produce a BunderstackEnvError with all issues,
not only the first one.
Describe keys for deployment
Hosting platforms read the committed blueprint to work out what an environment
needs before the first deploy. meta adds value-free metadata per key:
export const env = {
server: {
STRIPE_API_KEY: v.string(),
LOG_LEVEL: v.optional(v.string()),
},
client: {
PUBLIC_APP_URL: v.pipe(v.string(), v.url()),
},
meta: {
STRIPE_API_KEY: { description: 'Secret key from the Stripe dashboard' },
LOG_LEVEL: { sensitive: false, description: 'debug | info | warn | error' },
},
}Server keys are treated as secrets by default and client keys never are — a
PUBLIC_* value is compiled into the browser bundle, so declaring one sensitive
is an error. Descriptions are static prose, at most 200 characters.
Values never reach the blueprint. Only the key name, whether it is required, its scope, its secrecy, and its description do.
Use validated values
app.env.STRIPE_API_KEY
app.env.PUBLIC_APP_URL
api: (o) => ({
publicConfig: o.public.handler(({ context }) => ({
appUrl: context.env.PUBLIC_APP_URL,
})),
})Browser values
createClientEnv() validates only the client section. Server keys become
runtime traps if code tries to access them in a browser bundle.
import { createClientEnv } from 'bunderstack/env'
import { env } from './env-schema'
export const clientEnv = createClientEnv({
...env,
runtimeEnv: import.meta.env,
})Built-in values
Bunderstack also understands database, auth, Redis, email, and storage
variables used by its own facilities. Production requires a secure
AUTH_SECRET. Database URLs have adapter-specific development defaults; Redis
is required when separate worker processes publish realtime changes.
import { BunderstackEnvError } from 'bunderstack'
try {
await bunderstack({ schema, database, env }).start()
} catch (error) {
if (error instanceof BunderstackEnvError) {
console.error(error.issues)
}
}