Zero-dependency, strictly typed environment variable validator tailored for AWS Lambda
Features β’ Installation β’ Quick Start β’ Documentation β’ Examples β’ Contributing
Stop worrying about environment variables. Start building.
// Before: Manual parsing, no type safety, runtime errors
const port = parseInt(process.env.PORT || '3000');
const debug = process.env.DEBUG === 'true';
const origins = process.env.ALLOWED_ORIGINS?.split(',') || [];
// After: Type-safe, validated, zero boilerplate
const env = createEnv({
PORT: { type: 'number', default: 3000 },
DEBUG: { type: 'boolean', default: false },
ALLOWED_ORIGINS: { type: 'array', itemType: 'string', required: true },
});- β‘ Lambda-optimized: Built-in AWS Lambda environment support with type-safe access
- π Type-safe: Full TypeScript inference from schema to runtime
- π― Zero-config coercion: Automatic type conversion from environment strings
- π Secure by default: Automatic secret masking in error logs
- β Fail-fast: Validation at cold start, not during request handling
- πͺΆ Lightweight: ~5KB gzipped - minimal impact on Lambda cold starts
- π Zero dependencies: No external packages
- π‘οΈ Production-ready: Battle-tested validation and error handling
- π¨ Developer-friendly: Minimal boilerplate, maximum productivity
| Feature | lambda-env-schema | Zod | envalid |
|---|---|---|---|
| Lambda env vars | Built-in | Manual | Manual |
| Type coercion | Automatic | Manual (z.coerce) |
Automatic |
| Secret masking | Built-in | Manual | Manual |
| AWS validation | Built-in | Manual | Manual |
| Dependencies | Zero | Zero | 1+ |
| Focus | Lambda DX | General validation | Env vars |
| Cold start impact | Minimal | Moderate | Moderate |
npm install @kawaaaas/lambda-env-schemapnpm add @kawaaaas/lambda-env-schemayarn add @kawaaaas/lambda-env-schemaRequirements:
- Node.js 16+ (Lambda runtime: nodejs16.x or later)
- TypeScript 5.0+ (for type inference)
import { createEnv } from '@kawaaaas/lambda-env-schema';
// Define your schema
const env = createEnv({
// String with default value
LOG_LEVEL: { type: 'string', default: 'info' },
// Required string (marked as secret)
API_KEY: { type: 'string', required: true, secret: true },
// Number (auto-coerced from string)
PORT: { type: 'number', default: 3000 },
// Boolean (auto-coerced from string)
DEBUG: { type: 'boolean', default: false },
// Array (auto-split by comma)
ALLOWED_ORIGINS: { type: 'array', itemType: 'string', required: true },
// JSON (auto-parsed)
DATABASE_CONFIG: { type: 'json', required: true },
// AWS-specific validation (parsed types)
QUEUE_ARN: { type: 'sqs-queue-arn', required: true },
TABLE_ARN: { type: 'dynamodb-table-arn', required: true },
});
// Type-safe access with auto-completion
console.log(env.PORT); // number
console.log(env.API_KEY); // string
console.log(env.DEBUG); // boolean
console.log(env.ALLOWED_ORIGINS); // string[]
console.log(env.DATABASE_CONFIG); // unknown
// AWS-validated resources (parsed types)
console.log(env.QUEUE_ARN.queueName); // string (parsed from ARN)
console.log(env.TABLE_ARN.tableName); // string (parsed from ARN)
// Access AWS Lambda environment variables
console.log(env.aws.region); // string | undefined
console.log(env.aws.functionName); // string | undefinedimport { createEnv } from '@kawaaaas/lambda-env-schema';
import type { APIGatewayProxyHandler } from 'aws-lambda';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { SQSClient } from '@aws-sdk/client-sqs';
// β οΈ IMPORTANT: Call createEnv OUTSIDE the handler!
// This ensures validation runs during cold start (initialization phase),
// not during request handling. Invalid config fails fast before any
// requests are processed.
const env = createEnv({
// DynamoDB table with parsed ARN
TABLE_ARN: { type: 'dynamodb-table-arn', required: true },
// SQS queue with parsed ARN
QUEUE_ARN: { type: 'sqs-queue-arn', required: true },
// S3 bucket with validation
BUCKET_NAME: { type: 's3-bucket-name', required: true },
// API key (marked as secret)
API_KEY: { type: 'string', required: true, secret: true },
// Debug flag
DEBUG: { type: 'boolean', default: false },
});
// Initialize AWS clients with validated resources
const dynamodb = new DynamoDBClient({ region: env.aws.region });
const sqs = new SQSClient({ region: env.aws.region });
export const handler: APIGatewayProxyHandler = async (event) => {
// Use validated AWS resources with parsed properties
console.log('Table name:', env.TABLE_ARN.tableName);
console.log('Table region:', env.TABLE_ARN.region);
console.log('Queue name:', env.QUEUE_ARN.queueName);
console.log('Bucket:', env.BUCKET_NAME);
console.log('Function:', env.aws.functionName);
return {
statusCode: 200,
body: JSON.stringify({ message: 'Success' }),
};
};Environment variables are always strings. lambda-env-schema automatically converts them to the correct type:
// Environment: PORT=3000
const env = createEnv({
PORT: { type: 'number', default: 8080 },
});
console.log(typeof env.PORT); // "number"
console.log(env.PORT); // 3000const env = createEnv({
// Required: must be set in environment
API_KEY: { type: 'string', required: true },
// Optional with default: uses default if not set
PORT: { type: 'number', default: 3000 },
// Optional without default: can be undefined
CACHE_TTL: { type: 'number' },
});
// TypeScript types:
// env.API_KEY: string
// env.PORT: number
// env.CACHE_TTL: number | undefinedProtect sensitive values in error logs:
const env = createEnv({
API_KEY: { type: 'string', required: true, secret: true },
DATABASE_URL: { type: 'string', required: true, secret: true },
});
// If validation fails, secrets are automatically masked:
// β Environment variable "API_KEY" is required but not set
// (actual value is never logged)Access AWS Lambda runtime variables in a type-safe way:
const env = createEnv({
// Your custom variables
API_KEY: { type: 'string', required: true },
});
// AWS Lambda environment (automatically available)
console.log(env.aws.region); // AWS_REGION
console.log(env.aws.functionName); // AWS_LAMBDA_FUNCTION_NAME
console.log(env.aws.functionVersion); // AWS_LAMBDA_FUNCTION_VERSION
console.log(env.aws.memoryLimitInMB); // AWS_LAMBDA_FUNCTION_MEMORY_SIZE
console.log(env.aws.logGroupName); // AWS_LAMBDA_LOG_GROUP_NAME
console.log(env.aws.logStreamName); // AWS_LAMBDA_LOG_STREAM_NAMEAdd constraints to your environment variables:
const env = createEnv({
// String with enum
LOG_LEVEL: {
type: 'string',
enum: ['debug', 'info', 'warn', 'error'] as const,
default: 'info'
},
// String with pattern
EMAIL: {
type: 'string',
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
required: true
},
// String with length constraints
USERNAME: {
type: 'string',
minLength: 3,
maxLength: 20,
required: true
},
// Number with range
PORT: {
type: 'number',
min: 1024,
max: 65535,
default: 3000
},
// Array with length constraints
TAGS: {
type: 'array',
itemType: 'string',
minLength: 1,
maxLength: 10
},
});Validate AWS resource identifiers with 30+ built-in validators. AWS types come in two flavors:
Validation-only types (return string):
const env = createEnv({
// Identity & Access
AWS_REGION: { type: 'aws-region', required: true },
AWS_ACCOUNT_ID: { type: 'aws-account-id', required: true },
// Storage
BUCKET_NAME: { type: 's3-bucket-name', required: true },
// Database
TABLE_NAME: { type: 'dynamodb-table-name', required: true },
RDS_CLUSTER: { type: 'rds-cluster-id', required: true },
// Compute
FUNCTION_NAME: { type: 'lambda-function-name', required: true },
// Networking
VPC_ID: { type: 'vpc-id', required: true },
SUBNET_ID: { type: 'subnet-id', required: true },
SECURITY_GROUP: { type: 'security-group-id', required: true },
INSTANCE_ID: { type: 'ec2-instance-id', required: true },
// Other Services
EVENT_BUS: { type: 'event-bus-name', required: true },
API_ID: { type: 'api-gateway-id', required: true },
DIST_ID: { type: 'cloudfront-dist-id', required: true },
KMS_KEY: { type: 'kms-key-id', required: true },
PARAM_NAME: { type: 'ssm-parameter-name', required: true },
USER_ARN: { type: 'iam-user-arn', required: true },
});
console.log(env.BUCKET_NAME); // string
console.log(env.AWS_REGION); // stringParsed types (return structured objects with extracted properties):
const env = createEnv({
// ARNs with parsed components
ROLE_ARN: { type: 'iam-role-arn', required: true },
TABLE_ARN: { type: 'dynamodb-table-arn', required: true },
QUEUE_ARN: { type: 'sqs-queue-arn', required: true },
TOPIC_ARN: { type: 'sns-topic-arn', required: true },
FUNCTION_ARN: { type: 'lambda-function-arn', required: true },
KMS_ARN: { type: 'kms-key-arn', required: true },
SECRET_ARN: { type: 'secrets-manager-arn', required: true },
// URLs and URIs with parsed components
QUEUE_URL: { type: 'sqs-queue-url', required: true },
S3_ARN: { type: 's3-arn', required: true },
S3_URI: { type: 's3-uri', required: true },
DB_ENDPOINT: { type: 'rds-endpoint', required: true },
// Generic ARN parser
GENERIC_ARN: { type: 'arn', required: true },
});
// Access parsed properties
console.log(env.ROLE_ARN.accountId); // string
console.log(env.ROLE_ARN.roleName); // string
console.log(env.ROLE_ARN.path); // string | undefined
console.log(env.QUEUE_URL.accountId); // string
console.log(env.QUEUE_URL.region); // string
console.log(env.QUEUE_URL.queueName); // string
console.log(env.S3_ARN.bucketName); // string
console.log(env.S3_ARN.key); // string | undefinedAvailable validators:
- Identity & Access:
aws-region,aws-account-id,iam-role-arn,iam-user-arn - Storage:
s3-bucket-name,s3-arn,s3-uri - Database:
dynamodb-table-name,dynamodb-table-arn,rds-cluster-id,rds-endpoint - Compute:
lambda-function-name,lambda-function-arn - Messaging:
sqs-queue-url,sqs-queue-arn,sns-topic-arn - Security:
kms-key-id,kms-key-arn,secrets-manager-arn - Networking:
vpc-id,subnet-id,security-group-id,ec2-instance-id - Other:
event-bus-name,api-gateway-id,cloudfront-dist-id,ssm-parameter-name,arn
For comprehensive documentation including API reference, advanced usage, best practices, and troubleshooting, see:
- API Reference
- Schema Types
- Advanced Usage
- Error Handling
- Best Practices
- Troubleshooting
- TypeScript Support
Check out the basic example for a complete demonstration of all features:
cd examples/basic
cp .env.example .env
pnpm install
pnpm build
pnpm start// With DynamoDB
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
const env = createEnv({
TABLE_NAME: { type: 'string', required: true },
AWS_REGION: { type: 'string', default: 'us-east-1' },
});
const client = new DynamoDBClient({ region: env.AWS_REGION });// With Multiple Environments
const env = createEnv({
NODE_ENV: {
type: 'string',
enum: ['development', 'staging', 'production'] as const,
default: 'development'
},
API_ENDPOINT: { type: 'string', required: true },
});
if (env.NODE_ENV === 'production') {
// Production-specific logic
}// With CamelCase Naming
const env = createEnv(
{
API_KEY: { type: 'string', required: true },
DATABASE_URL: { type: 'string', required: true },
},
{ namingStrategy: 'camelCase' }
);
console.log(env.apiKey); // instead of env.API_KEY
console.log(env.databaseUrl); // instead of env.DATABASE_URLWe welcome contributions! Please see CONTRIBUTING.md for details on:
- Development setup
- Code style guidelines
- Testing requirements
- Pull request process
MIT Β© kawaaaas
- π¦ npm Package
- π Full Documentation
- π‘ Examples
- π€ Contributing Guide
- π Report Issues
- π¬ Discussions
Inspired by:
- Zod - TypeScript-first schema validation
- envalid - Environment variable validation
- t3-env - Type-safe environment variables
Made with β€οΈ for AWS Lambda developers