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
- Yarn
- pnpm
- Bun
npm create pikku@latest
yarn create pikku
pnpm create pikku@latest
bun create pikku@latest
You'll be asked:
- Project name - Choose a name for your project (default:
my-app) - Template - Select a runtime template:
express- Express server (recommended for beginners)fastify- Fastify servernextjs- Next.js App Routeraws-lambda- AWS Lambdacloudflare-workers- Cloudflare Workers- And many more...
- Package manager - Choose npm, yarn, or bun
- 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
- Yarn
- pnpm
- Bun
npm run start
yarn run start
pnpm run start
bun 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:
- npm
- Yarn
- pnpm
- Bun
npx pikku && npm run start
yarn dlx pikku && npm run start
pnpm dlx pikku && npm run start
bun x 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:
- Core Features - Learn about sessions, middleware, and auth
- WebSockets - Add real-time communication
- Workflows - Build multi-step processes with state management
- Queue Workers - Process background jobs
- Deploy Anywhere - Run on AWS Lambda, Cloudflare Workers, and more