pikku-concepts

$npx mdskill add pikkujs/pikku/pikku-concepts

Clarify Pikku's mental model for new projects and migrations.

  • Helps developers understand function types and project structure.
  • Integrates with HTTP, WebSocket, queues, and CLI transport mechanisms.
  • Decides relevance by detecting framework questions or migration needs.
  • Delivers clear conceptual overviews without executing wiring tasks.

SKILL.md

.github/skills/pikku-conceptsView on GitHub ↗
---
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).'
---

# Pikku Framework Concepts

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

```text
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:

1. **Services** — injected dependencies (logger, db, jwt, custom stores). See `pikku-services`.
2. **Data** — input from any source (HTTP body/query/params, WS message, queue payload, CLI args)
3. **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

| Generic Backend Concept                 | Pikku Equivalent                                                | Skill             |
| --------------------------------------- | --------------------------------------------------------------- | ----------------- |
| **Controller / Route Handler**          | `pikkuFunc` / `pikkuSessionlessFunc`                            | (this skill)      |
| **Route definition** (`GET /users/:id`) | `wireHTTP({ route, method, func })`                             | `pikku-http`      |
| **Middleware** (Express/Koa-style)      | `pikkuMiddleware`                                               | `pikku-security`  |
| **Auth Guard / Auth Middleware**        | `authBearer()` / `authCookie()` / `authApiKey()`                | `pikku-security`  |
| **Authorization / Permissions**         | `pikkuPermission` / `pikkuAuth`                                 | `pikku-security`  |
| **DTO / Request Validation**            | Standard Schema (Zod, Valibot, ArkType)                         | (this skill)      |
| **Dependency Injection**                | `pikkuServices` (singleton) + `pikkuWireServices` (per-request) | `pikku-services`  |
| **WebSocket handlers**                  | `wireChannel`                                                   | `pikku-websocket` |
| **Job Queue workers**                   | `wireQueueWorker`                                               | `pikku-queue`     |
| **Cron / Scheduled tasks**              | `wireScheduler`                                                 | `pikku-cron`      |
| **Module / Feature grouping**           | Tags + wiring files                                             | (this skill)      |
| **Error handling**                      | Throw typed errors (`NotFoundError`, `ForbiddenError`)          | (this skill)      |
| **Type-safe API client**                | `npx pikku prebuild` generates clients                          | (this skill)      |
| **Secrets / Config**                    | `wireSecret`, `wireVariable`, `services.variables`              | `pikku-config`    |

## Functions

Three main function types:

```typescript
// Requires authentication — receives session in wire context
const updateTodo = pikkuFunc<UpdateInput, TodoOutput>(
  async (services, data, wire) => {
    const session = await wire.session.get()
    return services.todoStore.update(data.id, data)
  }
)

// No authentication required
const listTodos = pikkuSessionlessFunc<ListInput, TodoListOutput>(
  async (services, data) => {
    return { todos: services.todoStore.list(data.filters) }
  }
)

// No input or output (for scheduled tasks, lifecycle hooks)
const cleanup = pikkuVoidFunc(async (services) => {
  services.todoStore.cleanOldItems()
})
```

Config object form (recommended):

```typescript
const createTodo = pikkuSessionlessFunc({
  title: 'Create Todo',
  description: 'Create a new todo item',
  input: CreateTodoInputSchema,
  output: CreateTodoOutputSchema,
  func: async ({ logger, todoStore }, { title, priority }) => {
    const todo = todoStore.createTodo(title, priority)
    logger.info(`Created todo: ${todo.id}`)
    return { todo }
  },
})
```

Full config options:

```typescript
pikkuFunc({
  title?: string,           // Human-readable name
  description?: string,     // What the function does
  version?: number,         // Contract version (see pikku-config for versioning)
  tags?: string[],          // For grouping and middleware targeting
  expose?: boolean,         // Allow external RPC calls (see pikku-rpc)
  remote?: boolean,         // Allow remote RPC calls
  mcp?: boolean,            // Expose as MCP tool (see pikku-mcp)
  auth?: boolean,           // Override default auth requirement
  input?: ZodSchema,        // Input validation schema
  output?: ZodSchema,       // Output validation schema
  permissions?: PermissionGroup,  // See pikku-security
  middleware?: PikkuMiddleware[], // See pikku-security
  func: async (services, data, wire) => { ... },
})
```

## Schemas (Validation)

Pikku uses Standard Schema — works with Zod, Valibot, ArkType:

```typescript
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:

```typescript
import '../../functions/.pikku/pikku-bootstrap.gen.js' // Generated — registers all wirings

const config = await createConfig()
const singletonServices = await createSingletonServices(config)

// Pick your runtime:
const server = new PikkuFastifyServer(
  config,
  singletonServices,
  createWireServices
)
// or: new PikkuExpressServer(config, singletonServices, createWireServices)
// or: pikkuAWSLambdaHandler(singletonServices)
// or: PikkuCloudflareHandler(singletonServices)
// or: pikkuNextHandler(singletonServices)

await server.init()
await server.start()
```

## Code Generation

Run `npx pikku prebuild` 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`:

```json
{
  "tsconfig": "./tsconfig.json",
  "srcDirectories": ["src"],
  "outDir": ".pikku"
}
```

## Project Structure Convention

```text
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`):

```typescript
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:

```typescript
const mockServices = {
  logger: new MockLogger(),
  todoStore: new MockTodoStore(),
}

// Call function directly — no HTTP, no framework
const result = await listTodos.func(mockServices, { userId: 'test' })
expect(result.todos).toHaveLength(3)
```

## Available Packages

### Runtime Adapters

| Package                       | Use Case                              |
| ----------------------------- | ------------------------------------- |
| `@pikku/express-server`       | Express standalone server             |
| `@pikku/express-middleware`   | Express as middleware in existing app |
| `@pikku/fastify-server`       | Fastify standalone                    |
| `@pikku/fastify-plugin`       | Fastify plugin                        |
| `@pikku/next`                 | Next.js API routes                    |
| `@pikku/aws-lambda`           | AWS Lambda handlers                   |
| `@pikku/cloudflare`           | Cloudflare Workers                    |
| `@pikku/uws-server`           | uWebSockets.js (high perf)            |
| `@pikku/modelcontextprotocol` | MCP server                            |

### Service Packages

| Package                  | Provides                           |
| ------------------------ | ---------------------------------- |
| `@pikku/jose`            | JWT (sign/verify) via jose library |
| `@pikku/schema-ajv`      | Schema validation via AJV          |
| `@pikku/schema-cfworker` | Schema validation for Cloudflare   |
| `@pikku/pino`            | Structured logging via Pino        |
| `@pikku/kysely`          | Type-safe SQL via Kysely (PostgreSQL, SQLite, MySQL) |
| `@pikku/redis`           | Redis client                       |
| `@pikku/queue-bullmq`    | Job queues via BullMQ              |
| `@pikku/queue-pg-boss`   | Job queues via PgBoss              |
| `@pikku/aws-services`    | AWS SDK (SQS, DynamoDB, etc.)      |

## Key Differences from Traditional Frameworks

1. **No decorators** — plain functions + explicit wiring, not `@Get()` or `@Injectable()`
2. **No classes required** — everything is functions and objects
3. **Transport is configuration, not code** — business logic doesn't know about HTTP/WS/etc.
4. **One function, many transports** — same function can serve HTTP, WebSocket, queue, and MCP simultaneously
5. **Generated type safety** — clients are auto-generated with full types, not manually maintained
6. **Schema-first validation** — Standard Schema (Zod/Valibot) replaces class-validator decorators

More from pikkujs/pikku

SkillDescription
pikku-addon'Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon, addon(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, and cross-project function sharing.
pikku-ai-agent'Use when building AI agents, chatbots, or LLM-powered assistants with Pikku. Covers pikkuAIAgent, tool registration, memory, streaming, and agent invocation.
pikku-ai-vercel'Use when setting up AI agent execution with the Vercel AI SDK in a Pikku app. Covers VercelAIAgentRunner for streaming and non-streaming AI agent steps.
pikku-ai-voice'Use when adding voice input (speech-to-text) or voice output (text-to-speech) to AI agents in a Pikku app. Covers voiceInput/voiceOutput middleware hooks and STT/TTS service interfaces.
pikku-auth-js'Use when integrating Auth.js (NextAuth) with a Pikku app. Covers createAuthHandler, createAuthRoutes, and Auth.js configuration.
pikku-aws'Use when setting up AWS services (S3, SQS, Secrets Manager) in a Pikku app. Covers S3Content for file storage, SQSQueueService for queues, and AWSSecrets for secret management.
pikku-backblaze'Use when setting up Backblaze B2 file storage in a Pikku app. Covers B2Content for file uploads, downloads, and signed URLs.
pikku-cli'Use when building CLI commands with Pikku. Covers wireCLI, pikkuCLICommand, subcommands, options, parameters, custom renderers, and nested command groups.
pikku-config'Use when managing secrets, environment variables, config, or OAuth2 credentials in a Pikku app. Covers wireSecret, wireVariable, wireOAuth2Credential, and typed config access.
pikku-cron'Use when adding scheduled tasks, recurring jobs, or cron-based automation to a Pikku app. Covers wireScheduler, cron expressions, scheduled task wire object, and scheduler middleware.