A type-safe environment variable management system for Vike applications that enables validation and transformation of environment variables using Zod schemas.
- 🔒 Type-safe: Fully typed environment variables with TypeScript support
- ✅ Validation: Schema-based validation using Zod
- 🔄 Transformation: Convert environment variables to appropriate types
- 🌐 Multiple Sources: Access variables from both process.env and import.meta.env
- 🛠️ Vite Plugin: Seamless integration with Vite and Vike
# npm
npm install vike-envz zod
# yarn
yarn add vike-envz zod
# pnpm
pnpm add vike-envz zod
Following a modular approach is recommended for better code organization and reusability:
Create a dedicated file for your environment schema that can be shared between your Vite config and server code:
// server/envz.ts
import type { EnvZ } from "vike-envz";
import { z } from "zod";
export const envSchema = {
// Build-time variables from import.meta.env
APP_VERSION: [z.string().nonempty(), "importMeta"],
// Runtime variables from process.env with type coercion
PORT: [z.coerce.number().positive().default(3000), "process"],
// Variables with default source ("all")
CANONICAL_URL: [z.string().nonempty()],
// With default values
DEBUG: [z.enum(['true', 'false']).transform(v => v === 'true').default('false')],
} satisfies EnvZ;
Use your shared schema in your Vite configuration:
// vite.config.ts
import { defineConfig } from 'vite';
import vike from 'vike/plugin';
import envZ from 'vike-envz/plugin';
import { envSchema } from './server/envz';
export default defineConfig({
plugins: [
vike(),
envZ({ envSchema }),
]
});
// server/index.ts
import express from 'express';
import { apply } from 'vike-server/express';
import { serve } from 'vike-server/express/serve';
import { getEnvZ } from "vike-envz";
import { envSchema } from './envz';
function startServer() {
// Get validated environment variables
const { PORT, DEBUG, CANONICAL_URL } = getEnvZ(envSchema);
const app = express();
// Apply vike middleware
apply(app);
// Use validated environment variables
if (DEBUG) {
console.log(`Server starting on port ${PORT}`);
console.log(`Canonical URL: ${CANONICAL_URL}`);
}
// Start server with validated PORT
return serve(app, { port: PORT });
}
export default startServer();
You can specify the source for each environment variable:
-
'process'
: Variables are read from Node.jsprocess.env
-
'importMeta'
: Variables are read from Vite'simport.meta.env
-
'all'
: Variables are read fromimport.meta.env
first, thenprocess.env
as fallback (default)
The library provides full type inference for your environment variables:
const env = getEnvZ({
// String validation
API_KEY: [z.string().min(1)],
// Number transformation
PORT: [z.coerce.number().positive()],
// Boolean transformation
DEBUG: [z.enum(['true', 'false']).transform(v => v === 'true')],
// URL validation
API_URL: [z.string().url()],
// With default values
TIMEOUT: [z.coerce.number().default(5000)],
});
// TypeScript will enforce correct types:
env.PORT.toFixed(2); // OK - PORT is a number
env.DEBUG && console.log('Debug mode enabled'); // OK - DEBUG is a boolean
env.TIMEOUT > 1000; // OK - TIMEOUT is a number with default
The library throws descriptive errors when environment variables fail validation:
try {
const env = getEnvZ(envSchema);
} catch (error) {
console.error('Environment validation failed:', error.message);
// e.g. "Error while checking for "PORT" from env: Expected string, received undefined"
process.exit(1);
}
MIT