| name | pikku-concepts |
| description | Foundational guide to Pikku framework concepts. Use this skill when working with any Pikku codebase, starting a new Pikku project, or migrating a backend to Pikku. Covers the core mental model, function types, project structure, code generation, testing, and how Pikku maps to traditional backend patterns. TRIGGER when: user asks "what is Pikku?", starts a new Pikku project, migrates from Express/NestJS/Hono, or needs to understand how Pikku works. DO NOT TRIGGER when: user is doing a specific wiring task (use the specific skill instead, e.g. pikku-http, pikku-websocket). |
| installGroups | ["core"] |
Pikku Framework Concepts
Agent Operating Procedure
Use this skill as an execution checklist, not reference material.
- Discover before editing. Prefer OpenCode tools such as
pikku-meta when available; otherwise run the relevant pikku meta ... --json command and inspect only the focused output you need.
- Identify the source files that own the behavior. Do not start by reading generated output,
.pikku, node_modules, vendored packages, or broad build artifacts.
- Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
- Validate with the narrowest relevant command first, then run
pikku-verify or pikku all when functions, wirings, schemas, or generated clients may have changed.
- If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
Pikku is a TypeScript framework that separates business logic from transport mechanisms. You define a function once, then wire it to HTTP, WebSocket, queues, schedulers, MCP, CLI, or RPC — without the function knowing how it's being called.
For deep-dive on each topic, see the dedicated skills:
- Wiring:
pikku-http, pikku-websocket, pikku-rpc, pikku-mcp, pikku-queue, pikku-cron, pikku-trigger, pikku-cli, pikku-ai-agent, pikku-workflow
- Infrastructure:
pikku-services, pikku-security, pikku-config
- Project introspection:
pikku-info
Core Mental Model
pikkuFunc (pure business logic)
│
├── wireHTTP → Express, Fastify, Next.js, Lambda, Cloudflare...
├── wireChannel → WebSocket (real-time)
├── wireQueueWorker → BullMQ, PgBoss (async jobs)
├── wireScheduler → Cron (scheduled tasks)
├── wireMCPTool → Model Context Protocol (AI tools)
├── wireCLI → CLI commands
├── wireTrigger → Event-driven (Redis pub/sub, PG LISTEN/NOTIFY)
├── pikkuAIAgent → AI agents / chatbots
├── pikkuWorkflow → Multi-step durable workflows
└── wire.rpc → Internal function-to-function calls
A pikkuFunc receives three things:
- Services — injected dependencies (logger, db, jwt, custom stores). See
pikku-services.
- Data — input from any source (HTTP body/query/params, WS message, queue payload, CLI args)
- Wire — transport context (session, channel, rpc, mcp, http, queue)
The function never imports Express, never reads req.body, never touches ws.send(). It just works with typed data and services.
Concept Mapping: Generic Backend → Pikku
Controllers/routes → pikkuFunc; middleware/auth/permissions → pikku-security; DI → pikku-services; transports (HTTP/WS/queue/cron) → their wire* + skill. For the full Generic Backend → Pikku mapping table (with side-by-side code examples), read references/concept-mapping.md.
Functions
Three main function types:
const updateTodo = pikkuFunc({
input: UpdateTodoInput,
output: TodoOutput,
func: async (services, data, wire) => {
const session = await wire.session.get()
return services.todoStore.update(data.id, data)
},
})
const listTodos = pikkuSessionlessFunc({
input: ListTodosInput,
output: TodoListOutput,
func: async (services, data) => {
return { todos: services.todoStore.list(data.filters) }
},
})
const cleanup = pikkuVoidFunc(async (services) => {
services.todoStore.cleanOldItems()
})
Services can be destructured inline in the func signature (e.g. async ({ logger, todoStore }, { title }) => ...). Full config options:
pikkuFunc({
title?: string,
description?: string,
version?: number,
tags?: string[],
expose?: boolean,
remote?: boolean,
mcp?: boolean,
auth?: boolean,
input?: ZodSchema,
output?: ZodSchema,
permissions?: PermissionGroup,
middleware?: PikkuMiddleware[],
func: async (services, data, wire) => { ... },
})
Schemas (Validation)
Pikku uses Standard Schema — works with Zod, Valibot, ArkType:
import { z } from 'zod'
const CreateTodoInputSchema = z.object({
title: z.string().min(1).max(200),
priority: z.enum(['low', 'medium', 'high']).optional(),
tags: z.array(z.string()).optional(),
})
Schemas serve triple duty: runtime validation, TypeScript types, and OpenAPI documentation.
Server Bootstrap
Every Pikku app follows the same bootstrap pattern regardless of runtime:
import '../../functions/.pikku/pikku-bootstrap.gen.js'
const config = await createConfig()
const singletonServices = await createSingletonServices(config)
const server = new PikkuFastifyServer(
config,
singletonServices,
createWireServices
)
await server.init()
await server.start()
Code Generation
Run npx pikku all to generate:
pikku-types.gen.ts — Typed function factories and wiring functions
pikku-fetch.gen.ts — Type-safe HTTP client
pikku-websocket.gen.ts — Type-safe WebSocket client
pikku-bootstrap.gen.js — Runtime initialization (auto-imports all wirings)
pikku-services.gen.ts — Service factory types
Config lives in pikku.config.json:
{
"tsconfig": "./tsconfig.json",
"srcDirectories": ["src"],
"outDir": ".pikku"
}
Project Structure Convention
src/
├── functions/ # Business logic (pikkuFunc definitions)
│ ├── todos.functions.ts
│ ├── auth.functions.ts
│ └── scheduled.functions.ts
├── wirings/ # Transport bindings
│ ├── todos.http.ts
│ ├── channel.wiring.ts
│ ├── scheduler.wiring.ts
│ └── queue.wiring.ts
├── schemas.ts # Zod/Valibot schemas
├── services.ts # Service factories (see pikku-services)
├── middleware.ts # Middleware definitions (see pikku-security)
├── permissions.ts # Permission definitions (see pikku-security)
└── .pikku/ # Generated (gitignored)
├── pikku-types.gen.ts
├── pikku-fetch.gen.ts
└── pikku-bootstrap.gen.js
Environment Variables
Never use process.env inside Pikku functions. Use the variables service (see pikku-config):
const apiKey = services.variables.get('API_KEY')
process.env belongs in server bootstrap code (start.ts) only.
Testing
Functions are easily testable because they're pure:
const mockServices = {
logger: new MockLogger(),
todoStore: new MockTodoStore(),
}
const result = await listTodos.func(mockServices, { userId: 'test' })
expect(result.todos).toHaveLength(3)
Available Packages
Pikku ships runtime adapters (@pikku/express-server, @pikku/fastify-server, @pikku/next, @pikku/aws-lambda, @pikku/cloudflare, @pikku/uws-server, @pikku/modelcontextprotocol, ...) and service packages (@pikku/jose, @pikku/schema-ajv, @pikku/pino, @pikku/kysely, @pikku/redis, @pikku/queue-bullmq, @pikku/queue-pg-boss, ...). For the full list with use cases, read references/packages.md.
Key Differences from Traditional Frameworks
- No decorators — plain functions + explicit wiring, not
@Get() or @Injectable()
- No classes required — everything is functions and objects
- Transport is configuration, not code — business logic doesn't know about HTTP/WS/etc.
- One function, many transports — same function can serve HTTP, WebSocket, queue, and MCP simultaneously
- Generated type safety — clients are auto-generated with full types, not manually maintained
- Schema-first validation — Standard Schema (Zod/Valibot) replaces class-validator decorators