Skip to main content

Pikku

One Function, Every Protocol

Write TypeScript functions once, trigger them with HTTP, WebSocket, cron jobs, queues, and more. Pikku is the swiss knife for building scalable applications with full type safety.

Same function can handle RPC, HTTP, SSE, WebSocket, CronJobs, and Queues
Auto-generated mini TypeScript SDKs for all transform with complete type safety
Deploy anywhere - Express, Lambda, Cloudflare, Next.js, Azure and more

All the tools needed for scaleable backend platforms

Build your backend with all the different tools you need, without having to learn a new framework for each decision

Built for Developers

Pikku provides everything you need to build robust, type-safe applications with minimal setup.

🎯

Fully Typed

Complete type safety from server to client with automatic type generation

🔐

Auth & Permissions

Built-in authentication, sessions, and granular permission system

Middleware

Hook into your functions with middleware for logging, custom validation, and more

⚙️

Service Injection

Simple dependency injection without decorators or magic

HTTP APIs

Build REST APIs and handle HTTP requests with full type safety and automatic OpenAPI generation.

Runs on:

Features:

  • Automatic OpenAPI documentation
  • Auth & Permissions
  • Middleware
  • Progressively enhancement to stream
server.ts
export const createTodo = pikkuFunc<
{ title: string; description: string },
{ id: string; title: string; createdBy: string }
>(async ({ logger }, data, session) => {
// userSession is passed as 3rd parameter
const todo = {
id: crypto.randomUUID(),
title: data.title,
description: data.description,
createdBy: session.userId,
completed: false,
createdAt: new Date().toISOString()
}

logger.info(`Todo created by ${session.userId}`)
return todo
})

addHTTPRoute({
method: 'post',
route: '/todos',
func: createTodo,
auth: true, // Requires authentication
docs: {
description: 'Create a new todo',
tags: ['todos'],
},
})

WebSocket & Real-time

Build real-time applications with typed WebSocket connections and pub/sub messaging.

Runs on:

Features:

  • Run locally or in a serverless environments
  • Route to functions based on message action
  • Built-in pub/sub system
server.ts
export const onConnect = pikkuChannelConnectionFunc<{ welcome: string }>(
async ({ logger, channel }, session) => {
logger.info(`User ${session.userId} connected to chat`)
channel.send({
welcome: `Welcome ${session.username}!`
})
}
)

export const sendMessage = pikkuChannelFunc<
{ message: string },
{ timestamp: string; from: string; message: string }
>(async ({ channel, eventHub }, data, session) => {
// userSession provides authenticated user info
const chatMessage = {
timestamp: new Date().toISOString(),
from: session.username,
message: data.message
}

await eventHub?.publish('chat', channel.channelId, chatMessage)
return chatMessage
})

addChannel({
name: 'chat',
route: '/chat',
onConnect,
auth: true, // Requires authentication
onMessageRoute: {
action: {
sendMessage,
},
},
})

Server-Sent Events

Stream real-time data to clients with automatic reconnection and progressive enhancement.

Runs on:

Features:

  • Simple streaming API
  • Provide realtime updates
  • Typed streaming data
  • Typed SSE Client
server.ts
export const streamUserActivity = pikkuFunc<
void,
AsyncGenerator<{ timestamp: string; activity: string; userId: string }>
>(async function* ({ logger }, data, session) {
// userSession provides authenticated user info
logger.info(`Starting activity stream for user ${session.userId}`)

let count = 0
while (true) {
yield {
timestamp: new Date().toISOString(),
activity: `User activity update #${++count}`,
userId: session.userId
}

await new Promise(resolve => setTimeout(resolve, 2000))
}
})

addHTTPRoute({
method: 'get',
route: '/stream/activity',
func: streamUserActivity,
auth: true, // Requires authentication
responseType: 'stream',
docs: {
description: 'Stream user activity updates',
tags: ['streaming'],
},
})

Scheduled Tasks

Run cron jobs and scheduled tasks that work across all deployment environments.

Runs on:

Features:

  • Cron expression support
  • Timezone handling
  • Error retry logic
  • Serverless friendly
example.ts
export const dailyReportTask = pikkuVoidFunc(
async ({ logger, emailService, analyticsService }) => {
try {
// Generate daily metrics report
const metrics = await analyticsService.getDailyMetrics()

// Send report to admin team
await emailService.sendReport({
to: 'admin@company.com',
subject: 'Daily Metrics Report',
data: {
totalUsers: metrics.activeUsers,
totalRequests: metrics.apiCalls,
avgResponseTime: metrics.avgResponseTime,
date: new Date().toISOString().split('T')[0]
}
})

logger.info(`Daily report sent - ${metrics.activeUsers} active users`)
} catch (error) {
logger.error('Failed to generate daily report:', error)
throw error // Will trigger retry logic
}
}
)

addScheduledTask({
name: 'dailyReport',
schedule: '0 8 * * *', // Daily at 8 AM
func: dailyReportTask,
timezone: 'America/New_York',
docs: {
description: 'Generate and send daily metrics report',
tags: ['reports', 'analytics'],
},
})

Background Queues

Process background jobs with reliable message delivery and retry logic.

Runs on:

?
?

Features:

  • Switch out different queue providers, unlocking scaling opportunities
  • Supports different queue providers, without locking you in with code.
example.ts
export const processImageResize = pikkuSessionlessFunc<
{ imageUrl: string; sizes: number[]; userId: string },
{ processedImages: Array<{ size: number; url: string }> }
>(async ({ logger, imageService, storageService }, data) => {
try {
logger.info(`Processing image resize for user ${data.userId}`)

const processedImages = []

for (const size of data.sizes) {
// Resize image to specified size
const resizedBuffer = await imageService.resize(data.imageUrl, size)

// Upload to storage
const uploadedUrl = await storageService.upload({
buffer: resizedBuffer,
filename: `${data.userId}/resized_${size}px.jpg`,
contentType: 'image/jpeg'
})

processedImages.push({ size, url: uploadedUrl })
}

logger.info(`Successfully processed ${processedImages.length} image sizes`)
return { processedImages }

} catch (error) {
logger.error('Failed to process image:', error)
throw error // Will trigger retry logic
}
})

addQueueWorker({
name: 'image-processing',
func: processImageResize,
concurrency: 3,
retryOptions: {
attempts: 3,
backoff: 'exponential',
delay: 2000,
},
})

RPC & Services

Build type-safe RPC services with automatic client generation and service discovery.

Runs on:

Features:

  • Automatic client generation
  • Service discovery
  • Type-safe service calls
  • Request/response validation
server.ts
export const getUserProfile = pikkuSessionFunc<
{ userId: string },
{ id: string; name: string; email: string; organizationId: string }
>(async ({ logger, userService }, { userId }, session) => {
// userSession provides authenticated user info
logger.info(`Getting profile for user ${userId} from org ${session.organizationId}`)

const user = await userService.findUserInOrganization({
userId,
organizationId: session.organizationId
})

if (!user) {
throw new Error('User not found or access denied')
}

return {
id: user.id,
name: user.name,
email: user.email,
organizationId: user.organizationId
}
})

export const updateUserProfile = pikkuSessionFunc<
{ userId: string; name?: string; email?: string },
{ success: boolean; updatedAt: string }
>(async ({ userService }, { userId, ...updates }, session) => {
await userService.updateUser(userId, updates, session.organizationId)

return {
success: true,
updatedAt: new Date().toISOString()
}
})

addRPCService({
name: 'user-service',
version: '1.0.0',
methods: {
getUserProfile,
updateUserProfile,
},
auth: true
})

Deploy Anywhere, Scale Everywhere

From serverless functions to traditional servers, from SQS Lambda queues to serverless WebSockets - your Pikku code runs everywhere without modification.

☁️

Serverless First

AWS Lambda, Cloudflare Workers, Vercel Functions - deploy with zero configuration

SQS Lambda Queues
Serverless WebSockets
Scheduled Functions
🖥️

Traditional Servers

Express, Fastify, Next.js - integrate with existing applications seamlessly

Middleware Integration
Docker Deployment
Kubernetes Ready
🔧

Custom Runtimes

Build your own runtime adapter for any environment or platform

Custom HTTP Handlers
WebSocket Adapters
Scheduler Handlers
🚀It's all an adapter away

Pikku glues together the pieces you need, so you can build an adaptor to run in any environment.