Extensible middleware system for transforming and enhancing validated environment objects with custom functionality. Middleware allows customization of the final environment object with additional properties and behaviors.
Creates a strict proxy that prevents access to unvalidated variables and protects against mutations.
/**
* Creates strict proxy that prevents access to unvalidated variables
* Throws errors for undefined variables or mutation attempts
* @param envObj - Validated environment object
* @param rawEnv - Original raw environment object
* @param options - Configuration for proxy behavior
* @returns Proxy object with strict access controls
*/
function strictProxyMiddleware<T extends object>(
envObj: T,
rawEnv: unknown,
options?: StrictProxyMiddlewareOptions
): Proxy<T>;
interface StrictProxyMiddlewareOptions {
/** Additional properties that can be inspected/enumerated */
extraInspectables?: string[];
}Usage Examples:
import { customCleanEnv, str, strictProxyMiddleware } from "envalid";
// Using strict proxy middleware directly
const env = customCleanEnv(
process.env,
{
DATABASE_URL: str(),
API_KEY: str(),
},
(cleanedEnv, rawEnv) => {
return strictProxyMiddleware(cleanedEnv, rawEnv, {
extraInspectables: ["customProperty"]
});
}
);
// Attempting to access undefined variables throws an error
try {
console.log(env.UNDEFINED_VAR); // Throws error
} catch (error) {
console.error("Cannot access unvalidated variable");
}
// Attempting to modify throws an error
try {
env.DATABASE_URL = "new-url"; // Throws error
} catch (error) {
console.error("Environment is immutable");
}Adds convenient NODE_ENV-based boolean properties for environment detection.
/**
* Adds NODE_ENV convenience properties (isDev, isProd, isTest, etc.)
* Computes NODE_ENV from validated or raw environment
* @param envObj - Validated environment object
* @param rawEnv - Original raw environment object
* @returns Environment object with accessor properties
*/
function accessorMiddleware<T>(
envObj: T,
rawEnv: unknown
): T & CleanedEnvAccessors;
interface CleanedEnvAccessors {
/** True if NODE_ENV === 'development' */
isDevelopment: boolean;
/** Alias for isDevelopment */
isDev: boolean;
/** True if NODE_ENV === 'test' */
isTest: boolean;
/** True if NODE_ENV === 'production' */
isProduction: boolean;
/** Alias for isProduction */
isProd: boolean;
}Usage Examples:
import { customCleanEnv, str, accessorMiddleware } from "envalid";
// Using accessor middleware
const env = customCleanEnv(
process.env,
{
DATABASE_URL: str(),
API_KEY: str(),
},
(cleanedEnv, rawEnv) => {
return accessorMiddleware(cleanedEnv, rawEnv);
}
);
// Use convenience properties
if (env.isDevelopment) {
console.log("Running in development mode");
}
if (env.isProd) {
console.log("Production configuration active");
}
// Environment detection without NODE_ENV validation
const config = {
enableDebugLogs: env.isDev,
enableAnalytics: env.isProd,
skipAuth: env.isTest,
};Applies both accessor and strict proxy middleware in the standard order used by cleanEnv.
/**
* Applies both accessor and strict proxy middleware
* Default middleware chain used by cleanEnv
* @param cleanedEnv - Validated environment object
* @param rawEnv - Original raw environment object
* @returns Environment with accessors and strict proxy protection
*/
function applyDefaultMiddleware<T>(
cleanedEnv: T,
rawEnv: unknown
): StrictProxy<T & CleanedEnvAccessors>;
type StrictProxy<T> = Proxy<T>;Usage Examples:
import { customCleanEnv, str, applyDefaultMiddleware } from "envalid";
// Manually applying default middleware (equivalent to cleanEnv)
const env = customCleanEnv(
process.env,
{
DATABASE_URL: str(),
PORT: num({ default: 3000 }),
},
applyDefaultMiddleware
);
// Same as using cleanEnv directly
// const env = cleanEnv(process.env, { DATABASE_URL: str(), PORT: num({ default: 3000 }) });import { customCleanEnv, str, accessorMiddleware } from "envalid";
const loggingMiddleware = <T>(cleanedEnv: T, rawEnv: unknown) => {
const withAccessors = accessorMiddleware(cleanedEnv, rawEnv);
return {
...withAccessors,
log: (level: string, message: string) => {
const timestamp = new Date().toISOString();
const env = withAccessors.isDev ? "DEV" : "PROD";
console.log(`[${timestamp}] [${env}] [${level}] ${message}`);
}
};
};
const env = customCleanEnv(
process.env,
{ DATABASE_URL: str() },
loggingMiddleware
);
env.log("INFO", "Application starting");import { customCleanEnv, str, num, bool, accessorMiddleware } from "envalid";
const configMiddleware = <T>(cleanedEnv: T, rawEnv: unknown) => {
const withAccessors = accessorMiddleware(cleanedEnv, rawEnv);
return {
...withAccessors,
getConfig: () => ({
database: {
url: cleanedEnv.DATABASE_URL,
pool: withAccessors.isProd ? 20 : 5,
},
server: {
port: cleanedEnv.PORT,
cors: withAccessors.isDev,
},
features: {
analytics: withAccessors.isProd,
debugLogs: withAccessors.isDev,
}
})
};
};
const env = customCleanEnv(
process.env,
{
DATABASE_URL: str(),
PORT: num({ default: 3000 }),
},
configMiddleware
);
const appConfig = env.getConfig();import { customCleanEnv, str, strictProxyMiddleware } from "envalid";
const validationMiddleware = <T>(cleanedEnv: T, rawEnv: unknown) => {
// Additional validation after parsing
if (cleanedEnv.DATABASE_URL && !cleanedEnv.DATABASE_URL.startsWith("postgresql://")) {
throw new Error("DATABASE_URL must be a PostgreSQL connection string");
}
const strictEnv = strictProxyMiddleware(cleanedEnv, rawEnv);
return {
...strictEnv,
validate: () => {
console.log("All environment variables validated successfully");
return true;
}
};
};
const env = customCleanEnv(
process.env,
{ DATABASE_URL: str() },
validationMiddleware
);import { customCleanEnv, str, accessorMiddleware } from "envalid";
const cachingMiddleware = <T>(cleanedEnv: T, rawEnv: unknown) => {
const withAccessors = accessorMiddleware(cleanedEnv, rawEnv);
const cache = new Map<string, any>();
return {
...withAccessors,
getCached: <V>(key: string, factory: () => V): V => {
if (!cache.has(key)) {
cache.set(key, factory());
}
return cache.get(key);
},
clearCache: () => cache.clear(),
};
};
const env = customCleanEnv(
process.env,
{ DATABASE_URL: str() },
cachingMiddleware
);
// Cache expensive computations based on environment
const dbPool = env.getCached("dbPool", () => {
return createDatabasePool(env.DATABASE_URL);
});import { customCleanEnv, str, accessorMiddleware, strictProxyMiddleware } from "envalid";
// Compose multiple middleware functions
const composedMiddleware = <T>(cleanedEnv: T, rawEnv: unknown) => {
// Apply middleware in order
let env = accessorMiddleware(cleanedEnv, rawEnv);
// Add custom properties
env = {
...env,
startTime: Date.now(),
version: "1.0.0",
};
// Apply strict proxy last to protect everything
return strictProxyMiddleware(env, rawEnv);
};
const env = customCleanEnv(
process.env,
{ API_KEY: str() },
composedMiddleware
);type StrictProxy<T> = Proxy<T>;
interface StrictProxyMiddlewareOptions {
extraInspectables?: string[];
}
interface CleanedEnvAccessors {
isDevelopment: boolean;
isDev: boolean;
isTest: boolean;
isProduction: boolean;
isProd: boolean;
}