Environment Parser
Baeta provides a type-safe environment variable parser that validates and transforms environment variables at runtime.
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",
}),
};
Supported types
stringnumberboolean
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; // Error: 'string | undefined' is not assignable to 'string'
Error handling
The parser throws on:
- a required variable that's missing,
- a type conversion failure (e.g.
"abc"for anumber), - an error thrown by a custom
resolver.
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",
}),
};
A larger example
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
The first argument to createEnvParser is a (key) => string | undefined lookup — point it at whichever environment your runtime exposes.
Node.js
process.env:
const parse = createEnvParser((key) => process.env[key]);
Deno
Deno.env:
const parse = createEnvParser((key) => Deno.env.get(key));
Bun
Bun exposes both process.env (Node-compatible) and 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
Vite exposes vars via import.meta.env:
const parse = createEnvParser((key) => import.meta.env[key]);
Next.js
Next.js inlines NEXT_PUBLIC_* variables at build time — they have to be accessed via literal process.env.NEXT_PUBLIC_* so the bundler can replace them. A small mapping keeps the parser API usable:
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,
}),
};