Ultra‑light dependency‑injection container and environment variable loader/validator for AWS Lambda — zero cold‑start tax, zero runtime dependencies.
λ + DI = lambdi – pronounced “lam‑dee”.
container: Ultra-light, type-safe dependency injection for AWS Lambda (and beyond)loadenv: Fast, type-safe environment variable validation using Zod Mini
- Bundle sizes
- ESM-only exports
- Why lambdi?
- Features
- Installation
- Quick start: Dependency Injection (
container) - Environment variable validation (
loadenv) - API
- Example: Scoped per-request objects
- Testing pattern
- License
Build complete: dist/index.{mjs}, dist/container.js, dist/loadenv.js
• dist/index.mjs: 17.3 KB
• dist/container.js: 598 B
• dist/loadenv.js: 16.8 KB
All entrypoints are ESM, with subpath exports for each module:
"exports": {
".": "./dist/index.mjs",
"./di": "./dist/container.js",
"./loadenv": "./dist/loadenv.js"
}Import examples:
// Main entry (everything)
import { container, token, loadenv, envSchema } from '@bmz_1/lambdi';
// Dependency Injection only
import { container, token } from '@bmz_1/lambdi/di';
// Environment helpers only
import { loadenv, envSchema } from '@bmz_1/lambdi/loadenv';| Pain point | lambdi’s fix | Cost |
|---|---|---|
| Heavy AWS clients re‑initialised every warm invocation | Process‑wide singleton container caches them automatically | < 1 µs per resolve() |
| DI frameworks add 400 KB to bundle | lambdi dual build is < 8 KB gzipped |
No cold‑start hit |
| Key‑string collisions between libraries | Typed tokens (unique symbols) stop clashes |
Compile‑time safety |
| Resource leaks in tests | Optional disposeAll() cleans up |
Single loop call |
| Hard‑to‑mock singletons | createScope() spins fresh child containers |
Zero extra deps |
- 🐑 Singleton DI container that survives warm Lambda invocations
- 🔑 Type‑safe tokens prevent collisions & give IntelliSense
- ⚡ Promise deduplication – async factories run once even under high concurrency
- 🧹 Disposal hooks & scoped containers for clean tests and per‑request objects
- 🪶 Tiny bundle – < 8 KB gzipped, no runtime deps
npm install @bmz_1/lambdiNode 20+ is required (matches current AWS Lambda runtimes).
import { container, token } from '@bmz_1/lambdi';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
// create a typed token once – no string collisions
const DDB = token<DynamoDBClient>('ddb');
// Register dependencies at startup (cold start)
container.register(DDB, () => new DynamoDBClient({ region: 'us-east-1' }));
export const handler = async () => {
// Resolve previously registered dependency - cached for warm invocations
const ddb = await container.resolve(DDB);
/* … use ddb … */
return { ok: true };
};Validate and type-check your environment variables at startup using Zod Mini schemas.
- Throws if any required variable is missing or invalid.
- Returns a fully typed, validated object.
- Works with both main and subpath imports.
Usage:
import { loadenv, envSchema } from '@bmz_1/lambdi';
// or: import { loadenv, envSchema } from '@bmz_1/lambdi/loadenv';
import * as z from '@zod/mini';
const schema = envSchema({
DATABASE_URL: z.string(),
PORT: z.string().optional(),
DEBUG: z.boolean().optional(),
});
const env = loadenv(schema);
// env.DATABASE_URL is string
// env.PORT is string | undefined
// env.DEBUG is boolean | undefined
console.log('Database URL:', env.DATABASE_URL);You can also pass a custom source object (e.g., for tests):
const env = loadenv(schema, { DATABASE_URL: 'sqlite://:memory:' });| Item | Description |
|---|---|
container |
Process‑wide singleton Container instance |
token<T>(description) |
Create an InjectionToken<T> (unique symbol) |
container.register(token, factory, options?) |
Register a factory function for a token |
container.registerValue(token, value, options?) |
Register an existing value for a token |
container.resolve(token) |
Resolve a previously registered dependency |
container.createScope() |
Child container inheriting parent singletons |
container.disposeAll() |
Run disposers, clear cache (useful in tests) |
FactoryOptions.dispose(value) |
Optional disposer registered per singleton |
| Item | Description |
|---|---|
loadenv(schema, [src]) |
Validate and load env vars from process.env or custom src |
envSchema(defs) |
Build a Zod Mini schema for your environment variables |
import { container, token } from '@bmz_1/lambdi';
import { v4 as uuid } from 'uuid';
const REQUEST_ID = token<string>('request‑id');
export const handler = async (event) => {
const scope = container.createScope();
scope.register(REQUEST_ID, () => uuid());
return doWork(event, scope);
};
async function doWork(event, scope) {
const reqId = await scope.resolve(REQUEST_ID);
console.log('request', reqId, event.body);
}import { beforeEach, afterEach, it, expect } from 'vitest';
import { Container, token } from '@bmz_1/lambdi';
const DUMMY = token<number>('dummy');
let c: Container;
beforeEach(() => {
c = new Container();
});
afterEach(async () => {
await c.disposeAll();
});
it('returns same instance', async () => {
c.register(DUMMY, () => 42);
const value = await c.resolve(DUMMY);
const again = await c.resolve(DUMMY);
expect(again).toBe(value);
});MIT © 2025 BMZ