{"content":"# Type-Safe Environment Configuration with better-env\n\nUse better-env config modules for type-safe server/public env access, feature flags, and either-or credential constraints.\n\n### Type-Safe Environment Configuration with better-env\n\nUse `better-env/config-schema` for typed environment configuration instead of maintaining a custom local env schema utility.\n\n### Install better-env\n\n```bash\nbun add better-env\nbunx skills add neondatabase/better-env -a cursor -a codex -y\n```\n\n### Define feature-level config modules\n\nCreate config modules in `src/lib/*/config.ts`.\n\n```typescript\n// src/lib/db/config.ts\nimport { configSchema, server } from \"better-env/config-schema\";\n\nexport const databaseConfig = configSchema(\"Database\", {\n  url: server({ env: \"DATABASE_URL\" }),\n});\n```\n\nThen consume values from your module instead of reading `process.env` directly:\n\n```typescript\n// src/lib/db/client.ts\nimport { Pool } from \"pg\";\nimport { databaseConfig } from \"./config\";\n\nexport const pool = new Pool({\n  connectionString: databaseConfig.server.url,\n});\n```\n\n### Public values and feature flags\n\nUse `pub()` for client-safe values and `flag` when a feature is optional.\n\n```typescript\n// src/lib/sentry/config.ts\nimport { configSchema, pub, server } from \"better-env/config-schema\";\n\nexport const sentryConfig = configSchema(\n  \"Sentry\",\n  {\n    token: server({ env: \"SENTRY_AUTH_TOKEN\" }),\n    dsn: pub({\n      env: \"NEXT_PUBLIC_SENTRY_DSN\",\n      value: process.env.NEXT_PUBLIC_SENTRY_DSN,\n    }),\n    project: pub({\n      env: \"NEXT_PUBLIC_SENTRY_PROJECT\",\n      value: process.env.NEXT_PUBLIC_SENTRY_PROJECT,\n    }),\n  },\n  {\n    flag: {\n      env: \"NEXT_PUBLIC_ENABLE_SENTRY\",\n      value: process.env.NEXT_PUBLIC_ENABLE_SENTRY,\n    },\n  },\n);\n```\n\n### Either-or credentials\n\nUse `oneOf` when at least one credential must be configured.\n\n```typescript\n// src/lib/ai/config.ts\nimport { configSchema, oneOf, server } from \"better-env/config-schema\";\n\nexport const aiConfig = configSchema(\n  \"AI\",\n  {\n    oidcToken: server({ env: \"VERCEL_OIDC_TOKEN\" }),\n    gatewayApiKey: server({ env: \"AI_GATEWAY_API_KEY\" }),\n  },\n  {\n    constraints: (s) => [oneOf([s.oidcToken, s.gatewayApiKey])],\n  },\n);\n```\n\n### Optional fields and schema validation\n\nYou can keep optional env vars and custom validation with Zod.\n\n```typescript\nimport { z } from \"zod\";\nimport { configSchema, server } from \"better-env/config-schema\";\n\nexport const resendConfig = configSchema(\"Resend\", {\n  apiKey: server({ env: \"RESEND_API_KEY\" }),\n  fromEmail: server({\n    env: \"RESEND_FROM_EMAIL\",\n    schema: z\n      .string()\n      .regex(\n        /^.+\\s<.+@.+\\..+>$/,\n        'Must match \"Name <email@domain.com>\" format.',\n      ),\n  }),\n});\n```"}