Skip to main content
Stop copying functions across protocols

One function.
Every protocol.

Write your backend once. Pikku handles HTTP, WebSocket, queues, cron, CLI, and AI agents — all from the same function, with the same auth and middleware.

  • Unified backend logic across all protocols
  • Serverless or server — deploy anywhere without code changes
  • Full TypeScript type safety end-to-end
  • Production-ready with sessions, auth, and middleware

$ npm create pikku@latest  ·  MIT  ·  Open Source

Built with Pikku

AgreeWeHeyGermanymartaBambooRoseCalligraphy Cut

The Problem

You've written this function more than once.

And you'll write it again. Here's why.

Every protocol is another copy

HTTP, WebSocket, queues, CLI, SSE — each one gets its own handler. They start as copies but drift apart. Auth gets fixed in one place, not the others. Bugs multiply in silence.

Workflows, agents, and MCP are always extra work

Wiring a function to a workflow engine, an AI agent, or an MCP server means adapters, schema re-definitions, and another auth layer to maintain — even for logic you've already shipped.

Switching infrastructure means rewriting

Going from Express to Lambda, or Cloudflare Workers to a container, touches every handler. Your business logic ends up tangled with framework APIs you never wanted to care about.

One function. Every protocol.

Write your business logic once. Pikku wires it to any protocol — same auth, same permissions, no rewrites.

Write once

getCard.tsfunc.ts
const getCard = pikkuFunc({
title: 'Get Card',
description: 'Retrieve a card by ID',
func: async ({ db, audit }, { cardId }) => {
const card = await db.getCard(cardId)
await audit.log('getCard', { cardId })
return card
},
permissions: { user: isAuthenticated }
})
Same auth & permissions across all protocols
One place to fix bugs and add features
Type-safe inputs and outputs everywhere

Pick a protocol

HTTP API

Wiring code

HTTP APIwiring.ts
wireHTTP({
method: 'get',
route: '/cards/:cardId',
func: getCard
})
AI AgentsAlpha

Your functions are already agent tools

No adapters. No schema writing. No separate auth layer. Pass your existing Pikku functions directly — the agent gets your full backend.

src/agents/support.agent.ts
// These already exist in your backend — no changes needed
import { getCustomer, getOrders, createTicket } from './functions'

export const supportAgent = pikkuAgent({
name: 'support',
instructions: `You are a helpful support agent.
Look up the customer's account and recent orders.`,
tools: [getCustomer, getOrders, createTicket],
model: 'claude-sonnet-4-5'
})

// Wire it just like any HTTP endpoint
wireHTTP({
method: 'post',
route: '/api/chat',
func: supportAgent
})

Zero glue code

Pass any Pikku function as a tool — the agent inherits its type signature, description, and input schema automatically. No adapters, no wrappers, no manual schema definitions.

Auth follows the agent

Agents inherit the caller's session, permissions, and middleware. The same rules that protect your HTTP endpoints automatically protect every tool the agent can call — no separate security layer to build or maintain.

Any LLM, same interface

Bring OpenAI, Anthropic, or any provider. Pikku handles tool calling, streaming, and context — you just swap the model name.

Workflows

Multi-step processes that
survive anything

Write sequential logic like normal code. Pikku handles persistence, retries, and resumption — even across server restarts.

Deterministic replay

Completed steps are cached and never re-executed. A workflow that fails on step 4 resumes from step 4 — not from the beginning.

Sleep for hours, days, or weeks

workflow.sleep('5min') suspends execution without holding a server connection. Perfect for trial expirations, reminders, and follow-ups.

Survives restarts

State is persisted to PostgreSQL or Redis between steps. Deploy a new version mid-workflow and execution continues from where it left off.

src/workflows/onboarding.workflow.ts
export const onboardingWorkflow = pikkuWorkflowFunc(
async ({ workflow }, { email, userId }) => {
// Each step is persisted — safe to retry
const user = await workflow.do(
'Create user profile',
'createUserProfile',
{ email, userId }
)

await workflow.do(
'Add to CRM',
async () => crm.createUser(user)
)

// Suspend for 5 minutes — no server held
await workflow.sleep('Wait before welcome email', '5min')

await workflow.do(
'Send welcome email',
'sendEmail',
{ to: email, template: 'welcome' }
)

return { success: true }
}
)
Visual Control PlaneAlpha

See your entire backend
from one place

The Pikku Console gives every environment its own control plane — browse functions, run agents, manage secrets, and trigger workflows without writing a line of tooling code.

Pikku Console — browse and inspect all functions, wirings, and services
Browse all functions & wiringsAgent playgroundRun & visualize workflowsManage secrets & variablesPer-environment control
Addons

Install a backend feature
in one line

Addons are drop-in function packages — Stripe, Postgres, SendGrid, and more. Install, configure secrets, call via namespaced RPC. Fully typed.

src/wiring.ts
// One line per addon
wireAddon({
name: 'stripe',
package: '@pikku/addon-stripe'
})
wireAddon({
name: 'email',
package: '@pikku/addon-sendgrid',
secretOverrides: {
SENDGRID_API_KEY: 'MY_EMAIL_KEY'
}
})
src/functions/checkout.func.ts
// Call addon functions via namespaced RPC
const checkout = pikkuFunc({
func: async ({}, { plan }, { rpc }) => {
const session = await rpc.invoke(
'stripe:checkoutCreate',
{ plan, currency: 'usd' }
)
await rpc.invoke(
'email:mailSend',
{ to: session.email, template: 'receipt' }
)
return { url: session.url }
}
})

Drop-in, not bolt-on

Install a package, add one wireAddon() call, and its functions appear as namespaced RPC calls. No glue code, no adapters, no boilerplate.

Fully typed across boundaries

The CLI generates TypeScript definitions for every addon function. rpc.invoke('stripe:checkoutCreate', ...) is autocompleted with the exact input and output types.

Secrets you control

Addons declare what secrets they need. You map them to your own infrastructure with secretOverrides — no env vars leaking across packages.

Shared infrastructure

Addons reuse your existing logger, database, and services — no duplicate connections. Each addon gets its own namespace, so nothing collides.

Production-grade out of the box

Auth, validation, type-safe clients, middleware — all built in. No bolting on third-party packages or writing boilerplate for every new protocol.

Type-Safe Clients
Auto-generated from your function definitions
HTTP fetch, WebSocket, and RPC clients with full IntelliSense
Auth & Permissions
Built-in filters, no manual checks
Cookie, bearer, API key auth with fine-grained permissions
Services
Singleton and per-request injection
Database, logger, config—all type-safe and testable
Middleware
Before/after hooks across all protocols
Logging, metrics, tracing—work everywhere
Schema Validation
Auto-validate against TypeScript input schemas
Runtime validation catches errors before they hit your functions — supports Zod
Zero Lock-In
Standard TypeScript, no magic
Tiny runtime, bring your own database/logger/tools
Deploy Anywhere

Change your runtime.
Keep your functions.

Same code runs on Express, Fastify, AWS Lambda, Cloudflare Workers, Next.js, and more. Switching runtimes never requires touching your functions.

Plus any custom runtime via the adapter interface. Build your own →

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

pikku
~2.8MB
All functions, all protocols
📦

Microservices

Split by domain or feature

pikku --http-routes=/admin
pikku --tags=admin
~180KB
Only admin routes + dependencies
λ

Functions

One function per deployment

pikku --http-routes=/users/:id --types=http
~50KB
Single endpoint + minimal runtime

Built for the problems developers actually have

From the teams who switched

"So many places in my code base have like three entry points: CLI, public (sometimes protected) HTTP API and internally from within the API. Would be so nice having everything just an invoke away. With Nest it's a pain because you basically have to start the whole API up just to run CLI command."

Alex Harley

Co-founder @ Superbridge

"Ever been annoyed at having to write your code different in a Lambda than in an express handler? Pikku fixes that."

Christoph Sitter

CTO @ spot

Your next backend shouldn't have eight copies of the same function.

Write it once. Pikku wires it everywhere.

$ npm create pikku@latest

MIT Licensed  •  Open Source  •  5-minute setup