Tree-Shaking and Filtering
Pikku analyzes your wirings at build time to generate optimized entry points. This allows you to deploy the same codebase as a monolith, microservices, or individual functions without code changes.
How It Worksβ
When you run pikku, the CLI:
- Scans your codebase for
pikkuFuncdefinitions and wirings (wireHTTP,wireChannel,wireQueueWorker, etc.) - Applies filters based on CLI arguments (
--http-routes,--tags,--types) - Determines required services by analyzing which services each filtered function uses
- Generates an entry point that imports only the needed functions and services
// Your code - write once
export const getUser = pikkuFunc({
func: async ({ database }) => { /* ... */ }
})
export const deleteUser = pikkuFunc({
func: async ({ database, audit }) => { /* ... */ }
})
export const getStatus = pikkuFunc({
func: async ({ cache }) => { /* ... */ }
})
wireHTTP({ route: '/users/:id', func: getUser })
wireHTTP({ route: '/admin/users/:id', func: deleteUser })
wireHTTP({ route: '/status', func: getStatus })
Deploy Everythingβ
pikku
Generates an entry point that imports all three functions and instantiates all services: database, audit, cache.
Deploy Filtered Subsetβ
pikku --http-routes=/status
Generates an entry point that:
- β
Imports only
getStatus - β
Instantiates only
cacheservice - β Does NOT import
getUserordeleteUser - β Does NOT instantiate
databaseorauditservices
This means your health check endpoint has zero database connection overhead.
Filter Typesβ
By Routeβ
pikku --http-routes=/admin
Include only functions wired to routes matching /admin.
By Tagsβ
Tag your functions:
export const generateReport = pikkuFunc({
func: async ({ database }) => { /* ... */ },
docs: {
tags: ['reports', 'background']
}
})
Then filter:
pikku --tags=reports
By Protocol Typeβ
pikku --types=http # Only HTTP handlers
pikku --types=queue # Only queue workers
pikku --types=http,queue # Multiple types
Combining Filtersβ
pikku --http-routes=/admin --tags=payments
Filters use AND logic - functions must match all criteria.
Service Loadingβ
Pikku generates a services.ts file that dynamically loads only the services your filtered functions need:
// Generated services.ts
import { createDatabase } from '../services/database'
import { createCache } from '../services/cache'
export async function createServices() {
return {
cache: await createCache(),
// database and audit are NOT loaded
}
}
Why This Isn't More Magicalβ
Unlike dependency injection frameworks like NestJS, Pikku:
- Doesn't use decorators or reflection - Just static analysis of your code
- Doesn't automatically wire dependencies - You explicitly declare what each function needs
- Doesn't have a runtime DI container - Services are created once at startup, passed to functions
This is intentional. It means:
- Faster cold starts - No reflection or decorator processing at runtime
- Explicit dependencies - Clear what each function needs
- Better tree-shaking - Bundlers can eliminate unused code
- Simpler mental model - No magic, just functions and imports
Architecture Evolutionβ
The same codebase can be deployed differently as your needs change:
Bundle Only What You Deploy
Run your codebase as a monolith, as microservices, or as functions. Pikku creates the smallest bundle for your use case.
Monolith
Run everything in one process
Microservices
Split by domain or feature
Functions
One function per deployment
Phase 1: Monolithβ
pikku
Simple deployment, everything in one process.
Phase 2: Split Servicesβ
# Public API
pikku --tags=public
# Admin service
pikku --tags=admin
# Background workers
pikku --types=queue
Independent deployment and scaling per service.
Phase 3: Individual Functionsβ
# One Lambda per critical endpoint
pikku --http-routes=/users/:id --types=http
Maximum isolation and optimization.
No code changes required.
What Pikku Generatesβ
Pikku generates entry points with filtered imports. You then use your bundler of choice (esbuild, webpack, etc.) to create the final bundle.
// .pikku/entry.ts (generated)
import { getStatus } from '../src/functions/status'
import { createServices } from './services'
import { createHTTPHandler } from '@pikku/runtime-express'
const services = await createServices()
const handler = createHTTPHandler([
{ route: '/status', func: getStatus }
], services)
export default handler
The generated code is readable and straightforward - no magic.