The Two Factories
Two functions. Everything wired.
pikkuServices creates singletons at startup. pikkuWireServices creates per-request services. That's the whole model.
pikkuServices — Singletons
Created once at startup, shared across all requests. Database pools, loggers, JWT, third-party clients. Receives (config, existingServices).
pikkuWireServices — Per-request
Created fresh per HTTP request, queue job, CLI command, or WebSocket lifetime. Sessions, transactions, audit contexts. Receives (singletonServices, wire).
Destructure what you need
Functions declare dependencies explicitly. Pikku merges singleton + wire services so your function sees one flat object. Only pull what you use.
import { pikkuServices } from '#pikku'
export const createSingletonServices = pikkuServices(
async (config, existingServices) => {
const logger = new ConsoleLogger()
const database = new DatabasePool(config.database)
await database.connect()
const jwt = new JoseJWTService(
async () => [{ id: 'my-key', value: JWT_SECRET }],
logger
)
return {
config,
logger,
database,
jwt,
books: new BookService(),
}
}
)
import { pikkuWireServices } from '#pikku'
export const createWireServices = pikkuWireServices(
async (singletonServices, wire) => {
// Created fresh per request / queue job / CLI command
// Pikku merges these with singleton services automatically
return {
userSession: createUserSessionService(wire),
dbTransaction: new DatabaseTransaction(
singletonServices.database
),
}
}
)
Tree-Shaking
Only load what you actually use.
Pikku's CLI generates requiredSingletonServices — a map of which services your filtered functions need. Pair it with dynamic import() calls to skip everything else.
CLI scans destructuring
Pikku analyzes which services each function, middleware, and permission actually destructures from the services parameter.
Generates requiredSingletonServices
A boolean map marking each service true or false. Plus a RequiredSingletonServices type that narrows your factory's return type.
Dynamic imports skip unused services
Guard heavy imports with if (requiredSingletonServices.x). When a service isn't needed, the import is never executed — zero cold-start overhead.
// .pikku/pikku-services.gen.ts (auto-generated)
export const requiredSingletonServices = {
'database': true, // used by getUser, deleteUser
'audit': true, // used by deleteUser
'cache': false, // not used by any wired function
'jwt': true, // used by auth middleware
} as const
export type RequiredSingletonServices =
Pick<SingletonServices, 'database' | 'audit' | 'jwt'>
& Partial<Omit<SingletonServices, 'database' | 'audit' | 'jwt'>>
import { requiredSingletonServices } from '.pikku/pikku-services.gen.js'
import { pikkuServices } from '#pikku'
export const createSingletonServices = pikkuServices(
async (config) => {
const logger = new ConsoleLogger()
// Only create JWT if wired functions actually need it
let jwt: JWTService | undefined
if (requiredSingletonServices.jwt) {
const { JoseJWTService } = await import('@pikku/jose')
jwt = new JoseJWTService(keys, logger)
}
// Only connect to database if needed
let database: Database | undefined
if (requiredSingletonServices.database) {
database = await createDatabase(config.databaseUrl)
}
return { config, logger, jwt, database }
}
)
Faster cold starts
Health-check endpoints don't load your database driver. Payment endpoints don't load your email SDK.
Same codebase
Deploy as monolith, microservices, or individual functions — filter with CLI flags, no code changes.
Type-safe
RequiredSingletonServices narrows your factory return type so TypeScript catches missing services at compile time.
Start building in 5 minutes
Scaffold a project with services pre-configured. Add your own, destructure what you need, and deploy anywhere.
MIT Licensed · Works with Express, Fastify, Lambda & Cloudflare