Skip to main content

Pikku 0.8 — Our Biggest Release Yet!

· 6 min read
Updated for Pikku v0.11

This post contains code examples from Pikku 0.8. In v0.11, function signatures changed to use the wire parameter pattern. See the current documentation for updated examples.

After months of focused work, I'm thrilled to introduce Pikku 0.8 — the biggest update yet to the typed backend framework I’ve been building to simplify full-stack infrastructure.

This release marks a turning point: Pikku is no longer just for HTTP / Websockets and CronJobs — it’s now a complete platform for building scalable, typed, multi-transport backends. Whether you're targeting HTTP, queues, RPCs, or even AI agents — you now write your logic once, then connect it anywhere.


🧠 Quick Recap: What is Pikku?

At its core, Pikku is a developer-first framework for declaring backend logic as typed functions, then wiring those functions up to various transports — like HTTP, WebSocket, RPC, queues, or AI agent servers.

If you're familiar with function-based backends or serverless APIs, you’ll feel right at home — but Pikku brings full typings, permissions, and tooling to the party.

Here’s what a typical Pikku function looks like:

const updateTodo = pikkuFunc<Todo, { updated: boolean }>({
func: async ({ logger, todoService }, { todoId, ...data }, session) => {
await todoService.update(todoId, data)
return { updated: true }
},
permissions: {
canEditTodo: async ({ todoService }, data, session) => {
const { ownerId } = await todoService.get(data.todoId)
return ownerId === session.userId
}
}
})

Then you wire it up to the desired transport:

wireHTTP({
method: 'patch',
func: updateTodo,
})

📚 Learn more → Function Basics 📚 Learn more → HTTP APIs & Routing


⚡ New in 0.8: Remote Procedure Calls (RPCs)

You can now invoke Pikku functions via RPC — from clients, other services, or across your infrastructure.

If you're unfamiliar with RPCs, here's a primer: 🧠 What is RPC? → Martin Fowler

Server-side usage:

await rpc.invoke('send-Email', { type: 'todo-changed', todoId })

Typed client usage:

const rpc = new PikkuRPC()
const result = await rpc.invoke('update-todo', { type, todoId })

RPCs in Pikku:

  • Are typed with schema validation
  • Respect permissions and sessions
  • Work identically whether local or remote
  • Are tree-shakeable for minimal bundles

📚 Learn more → RPCs


🧵 Queue Workers

Pikku now supports async job queues with the same function-first philosophy.

Think of it like typed background workers — perfect for sending emails, processing images, or handling long-running tasks.

const queueWorker = pikkuSessionlessFunc<
{ message: string; fail: boolean },
{ result: string }
>(async ({}, data) => {
if (data.fail) throw new Error('Job failed intentionally')
return { result: `echo: ${data.message}` }
})

wireQueueWorker({
queueName: 'hello-world-queue',
func: queueWorker,
})

Run your preferred queue system (e.g., BullMQ or pg-boss):

const bullQueueWorkers = new BullQueueWorkers(
{},
singletonServices,
createSessionServices
)
await bullQueueWorkers.registerQueues()

Client usage:

// The QueueService is literally just a typed wrapper
const queueService = new PikkuQueue(new BullQueueService())
await queueService.add('hello-world-queue', { message: 'Hello', fail: false })

📚 Learn more → Queues


🤖 MCP Server: Build Agent-Compatible Backends

The MCP Server allows your Pikku functions to be used by AI agents — as tools, resources, or prompt generators. You can build your own local agent server, integrate with LLMs, or support emerging agent standards.

If you're new to agents or tool-calling: 🧠 What are AI agents? → LangChain Blog 🧠 Tool-Use in LLMs → OpenAI


Tools

Declare a function as a callable “tool”:

const sayHello = pikkuMCPToolFunc<{ name?: string }>(
async ({ logger }, { name = 'World' }) => {
logger.info(`Hello ${name}`)
return [{ type: 'text', text: `Hello, ${name}!` }]
}
)

wireMCPTool({
name: 'sayHello',
description: 'Greet someone with a friendly message',
func: sayHello,
})

📚 Learn more → MCP Tools


Resources

Expose structured data as accessible agent resources:

const getUserInfo = pikkuMCPResourceFunc<{ userId: string }>(
async ({ users }, { userId }) => {
const user = users[userId]
if (!user) throw new NotFoundError(`User not found`)
return [{ uri: `getUserInfo/${userId}`, text: JSON.stringify(user) }]
}
)

📚 Learn more → MCP Resources


Prompts

Dynamically generate prompts for LLMs:

const dynamicPromptGenerator = pikkuMCPPromptFunc<{
topic: string
complexity: 'beginner' | 'intermediate' | 'advanced'
includeExamples?: boolean
}>(async ({ logger }, { topic, complexity, includeExamples }) => {
let content = `# Learning ${topic} (${complexity})\n\n`
content += complexity === 'beginner'
? `Start with the basics.\n`
: complexity === 'intermediate'
? `Go deeper.\n`
: `Covering edge cases.\n`

if (includeExamples) content += `\n## Examples\n- Example 1\n- Example 2\n`
return [{ role: 'user', content: { type: 'text', text: content } }]
})

📚 Learn more → MCP Prompts


Running the MCP Server

const server = new PikkuMCPServer({
name: 'pikku-mcp-server',
version: '1.0.0',
mcpJsonPath: join(__dirname, '../../functions/.pikku/mcp.gen.json'),
capabilities: { logging: {}, tools: {}, resources: {}, prompts: {} },
}, singletonServices)

await server.init()
await server.connect(new StdioServerTransport())
server.wrapLogger()

📚 Learn more → MCP Server


🎯 Service Shaking: Smarter, Smaller Builds

Think tree-shaking, but for backend services.

You can now exclude unused services from your production bundle using tag-based CLI filters:

pikku --tags 'web,queue'

This tells Pikku to only include services used by functions tagged with web or queue.

Benefits:

  • Smaller bundles
  • Lazy-loaded service dependencies
  • More precise deployments
if (singletonServices.jwt) {
const { JoseJWTService } = await import('@pikku/jose')
jwt = new JoseJWTService(...)
}

📚 Learn more → Service Shaking


🚧 Coming Next

We consider Pikku 0.8 feature complete, in the sense we won't be adding more transports or core library changes.

In the near future, we’ll focus on:

  • 🐛 Fixing minor bugs
  • 🧑🏽‍💻 Adding APIs to exising wrappers like Queues or oAuth/Notifications to MCP
  • 🧾 Improving docs and examples
  • 💬 Expanding tutorials and templates
  • ✨ Polish across CLI and DX

If you're building something and get stuck — please open an issue or ask us on GitHub Discussions.


🔍 Peek: AI-Assisted Lookup One of the things I love about Pikku is that everything is statically documented — from services to queues to functions.

I’m exploring the idea of a Pikku-powered MCP agent — a local AI assistant that gives instant answers like:

“What queues are available in this service?” “Which function handles updating a todo?”

No need to crawl the codebase or guess — just ask. If this sounds interesting, stay tuned.


🙌 Final Thoughts

Pikku 0.8 isn’t just a version bump — it’s a foundational leap forward.

By treating functions as your backend contract, you gain:

  • ✅ Full TypeScript safety
  • 🔐 Auth & permission guards
  • 🔁 Transport-agnostic logic
  • 🧵 Queue & background job support
  • 🧠 Agent / AI integration
  • 🧼 Tree-shaken builds for tighter deployments

Whether you're building real-time apps, internal tools, microservices, or intelligent agents — Pikku 0.8 gives you a fast, typed, and scalable foundation.


🚀 Try It Now

Thanks for reading — I can't wait to see what you build.

— Yasser