@contract-kit/config
v1.0.0
Published
Environment-first configuration layer using Standard Schema for contract-kit
Downloads
795
Maintainers
Readme
@contract-kit/config
Environment-first configuration for Contract Kit apps and providers. Define the
variables your app owns, validate them at startup, and import a typed env
object throughout server code.
Installation
bun add @contract-kit/configFramework apps
Use createEnv(...) for application env files:
import { createEnv } from "@contract-kit/config";
import { z } from "zod";
export const env = createEnv({
server: {
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
DATABASE_URL: z.string().url(),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
},
clientPrefix: "NEXT_PUBLIC_",
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
},
runtimeEnv: process.env,
});server variables are server-only. If the returned env object is used in a
client runtime, reading a server-only key throws a descriptive error. client
variables must start with clientPrefix.
Server runtimes validate both server and client variables at startup.
Client runtimes validate only client variables, so missing server secrets do
not break public client bundles before the access guard can run.
createEnv(...) treats empty strings as undefined by default so schema
defaults work with .env files like LOG_LEVEL=.
Strict runtime envs
Use runtimeEnvStrict when your framework only bundles explicitly accessed env
vars:
export const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
},
clientPrefix: "NEXT_PUBLIC_",
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
},
runtimeEnvStrict: {
DATABASE_URL: process.env.DATABASE_URL,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
},
});Every key validated in the current runtime must be present on
runtimeEnvStrict, even if the value is undefined. Server runtimes require
all declared keys; client runtimes require only client keys. That catches
missing destructures during build without forcing server secrets into client
bundles.
Whole-object loading
Use defineEnv(...) when you already have a full object schema or need prefix
stripping:
import { defineEnv } from "@contract-kit/config";
import { z } from "zod";
const appEnv = defineEnv({
prefix: "APP_",
schema: z.object({
DATABASE_URL: z.string().url(),
SECRET_KEY: z.string().min(1),
}),
});
export const config = appEnv.load();This reads APP_DATABASE_URL and APP_SECRET_KEY, strips APP_, validates the
resulting object, and returns { DATABASE_URL, SECRET_KEY }.
Testing
Pass a custom env object instead of reading from process.env:
const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
},
runtimeEnv: {
DATABASE_URL: "postgres://localhost/test",
},
});Options
| Option | API | Description |
|--------|-----|-------------|
| server | createEnv | Server-only variable schemas |
| client | createEnv | Client-safe variable schemas |
| clientPrefix | createEnv | Required prefix for client variable names |
| runtimeEnv | createEnv | Runtime env object, usually process.env |
| runtimeEnvStrict | createEnv | Runtime env object that must include every declared key |
| emptyStringAsUndefined | both | Treat "" as undefined before validation |
| skipValidation | both | Skip validation for special build phases |
| onValidationError | both | Customize validation failures |
| onInvalidAccess | createEnv | Customize server-key access errors in client runtimes |
| schema | defineEnv | Whole-object Standard Schema |
| prefix | defineEnv | Prefix to filter and strip before validation |
Schema libraries
Works with any Standard Schema
library, including Zod, Valibot, and ArkType. createEnv(...) and
defineEnv(...).load() are synchronous and reject async schemas. Provider
config can use the exported async parser internally.
License
MIT
