Skip to main content

Getting Started with Pikku

Pikku is a TypeScript framework that lets you write backend logic once and wire it to any protocolβ€”HTTP, WebSockets, queues, scheduled tasks, and more. This guide will get you up and running in minutes.

Create Your Project​

Run the create command and follow the prompts:

npm create pikku@latest

You'll be asked:

  1. Project name - Choose a name for your project (default: my-app)
  2. Template - Select a runtime template:
    • express - Express server (recommended for beginners)
    • fastify - Fastify server
    • nextjs - Next.js App Router
    • aws-lambda - AWS Lambda
    • cloudflare-workers - Cloudflare Workers
    • And many more...
  3. Package manager - Choose npm, yarn, or bun
  4. Install dependencies - Yes to install and generate types automatically

The CLI will download the template, install dependencies, and run npx pikku to generate type-safe clients.

Navigate to your project:

cd my-app

Your Project Structure​

my-app/
β”œβ”€β”€ src/
β”‚ β”œβ”€β”€ functions.ts # Your business logic
β”‚ β”œβ”€β”€ wiring.ts # Connect functions to protocols
β”‚ └── services.ts # Services like database, logger
β”œβ”€β”€ .pikku/ # Generated files (don't edit)
β”‚ β”œβ”€β”€ http/ # HTTP types and wiring
β”‚ └── pikku-types.gen.ts # Generated types
β”œβ”€β”€ pikku.config.json # Pikku configuration
└── package.json

Explore the Example Functions​

The template includes example functions. Open src/functions.ts:

import { pikkuFunc } from './.pikku/pikku-types.gen'

// A simple hello world function
export const helloWorld = pikkuFunc(
async ({ logger }) => {
logger.info('Hello world called')
return { message: 'Hello World!' }
}
)

And they're already wired to HTTP in src/wiring.ts:

import { wireHTTP } from './.pikku/http/pikku-http.gen'
import { helloWorld } from './functions'

wireHTTP({
method: 'get',
route: '/hello-world',
func: helloWorld
})

Run Your Server​

Start the development server:

npm run start

Your API is now running at http://localhost:4002! Test it:

curl http://localhost:4002/hello-world

You should see:

{"message":"Hello World!"}

Add Your Own Function​

Let's create a new function. Edit src/functions.ts:

// Add this new function
interface GetUserInput {
userId: string
}

export const getUser = pikkuFunc<GetUserInput>(
async ({ logger }, { userId }) => {
logger.info(`Getting user ${userId}`)

return {
id: userId,
name: 'John Doe',
email: 'john@example.com'
}
}
)

Wire it to HTTP in src/wiring.ts:

import { getUser } from './functions'

wireHTTP({
method: 'get',
route: '/users/:userId',
func: getUser
})

Regenerate types and restart:

npx pikku && npm run start

Test your new endpoint:

curl http://localhost:4002/users/123

Create a Type-Safe Client​

Pikku automatically generates type-safe clients from your functions. Create a pikku-fetch.ts wrapper to use them:

import { pikkuFetch } from './.pikku/pikku-fetch.gen'

// Create a configured client instance
export const api = pikkuFetch({
baseURL: 'http://localhost:4002'
})

Now you can make type-safe API calls from your frontend or other services:

import { api } from './pikku-fetch'

// Type-safe HTTP calls - TypeScript knows the exact request/response types
const user = await api.fetch('/users/123', 'GET', null)
// user is typed as { id: string; name: string; email: string }

const greeting = await api.fetch('/hello-world', 'GET', null)
// greeting is typed as { message: string }

The client automatically:

  • Validates request and response types at compile time
  • Handles JSON serialization/deserialization
  • Provides autocomplete for all your endpoints
  • Catches type errors before runtime

Key Concepts​

1. Functions Are Protocol-Agnostic​

Your business logic is separate from HTTP, WebSockets, or any protocol. The same function can be wired to multiple protocols:

// Wire to HTTP
wireHTTP({ method: 'get', route: '/user/:id', func: getUser })

// Wire to WebSocket
wireChannel({
name: 'users',
onMessageWiring: { action: { getUser } }
})

// Wire to Queue
wireQueueWorker({ queue: 'process-user', func: getUser })

2. Type-Safe Clients​

Pikku generates type-safe clients automatically. After running npx pikku, you can use them:

import { pikkuFetch } from './.pikku/pikku-fetch.gen'

// Type-safe HTTP client
const response = await pikkuFetch.fetch('/users/123', 'GET', null)
// response is typed based on your function's return type

3. Services​

Services are dependency-injected into your functions:

export const getUser = pikkuFunc(
async ({ database, logger, config }, { userId }) => {
// Access services
const user = await database.users.findById(userId)
logger.info(`Found user ${user.name}`)

return user
}
)

Configure services in src/services.ts.

What's Next?​

Now that you have a working application, explore more:

Video Tutorial​