Environment Parser
Baeta provides a type-safe environment variable parser that helps you validate and transform environment variables at runtime while maintaining full type safety.
Installation
- yarn
- npm
- pnpm
- bun
yarn add @baeta/env
npm install @baeta/env
pnpm add @baeta/env
bun add @baeta/env
Basic Usage
import { createEnvParser } from "@baeta/env";
// Create a parser that reads from process.env
const parse = createEnvParser((key) => process.env[key]);
export const config = {
// Basic string parsing
apiKey: parse("API_KEY", {
type: "string",
required: true,
}),
// Number with default value
port: parse("PORT", {
type: "number",
default: 3000,
}),
// Boolean with custom resolver
isDevelopment: parse("NODE_ENV", {
type: "boolean",
default: true,
resolver: (value) => value === "development",
}),
};
Configuration Options
Types
The parser supports three basic types:
string
number
boolean
TypeScript Support
The parser provides full type inference:
const config = {
port: parse("PORT", {
type: "number",
default: 3000,
}),
cachePrefix: parse("CACHE_PREFIX", {
type: "string",
}),
};
// config.port is inferred as number
const port: number = config.port; // OK
const port: string = config.port; // Type error
const prefix: string = config.cachePrefix; // Possible type undefined is not assignable to string
Error Handling
The parser will throw descriptive errors when:
- A required variable is missing
- Type conversion fails (e.g., parsing a number)
- Custom resolver throws an error
Examples
Required Variables
const config = {
databaseUrl: parse("DATABASE_URL", {
type: "string",
required: true, // Will throw if not provided
}),
};
Default Values
const config = {
logLevel: parse("LOG_LEVEL", {
type: "string",
default: "info", // Uses 'info' if not provided
}),
};
Custom Resolvers
const config = {
isProduction: parse("NODE_ENV", {
type: "boolean",
default: false,
resolver: (value) => value === "production",
}),
};
Complex Configuration Example
Here's a more comprehensive example showing various use cases:
import { createEnvParser } from "@baeta/env";
const parse = createEnvParser((key) => process.env[key]);
export const config = {
// Basic required string
apiUrl: parse("API_URL", {
type: "string",
required: true,
}),
// Number with default
serverPort: parse("PORT", {
type: "number",
default: 80,
}),
// Boolean with custom resolver
isProduction: parse("NODE_ENV", {
type: "boolean",
default: false,
resolver: (value) => value === "production",
}),
// Optional string
cachePrefix: parse("CACHE_PREFIX", {
type: "string",
required: false,
}),
// Required with custom error handling
secretKey: parse("SECRET_KEY", {
type: "string",
required: true,
}),
};
Environment Sources
Node.js
Using Node's built-in process.env
:
const parse = createEnvParser((key) => process.env[key]);
Deno
Using Deno's built-in Deno.env:
const parse = createEnvParser((key) => Deno.env.get(key));
Bun
Using Bun's built-in process.env or Bun.env:
// Using process.env (Node.js compatible)
const parse = createEnvParser((key) => process.env[key]);
// Using Bun.env (Bun specific)
const parse = createEnvParser((key) => Bun.env[key]);
Vite
When using Vite, you can access environment variables through import.meta.env
:
const parse = createEnvParser((key) => import.meta.env[key]);
Next.js
Next.js requires special handling of environment variables during bundling. Only variables prefixed with NEXT_PUBLIC_
are included in the client bundle, and they must be accessed via process.env.NEXT_PUBLIC_*
directly in your code, since the bundler inlines them.
To work around this limitation, you can create a map of your environment variables:
const envMap = {
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
// ... other env vars
} as const;
const parse = createEnvParser((key) => envMap[key as keyof typeof envMap]);
export const config = {
apiUrl: parse("NEXT_PUBLIC_API_URL", {
type: "string",
required: true,
}),
};
Or you can edit the Webpack configuration to include a global map of public variables.