pikku-http

$npx mdskill add pikkujs/pikku/pikku-http

Connect Pikku functions to HTTP routes, APIs, and SSE streams.

  • Build REST endpoints, web servers, and server-sent events.
  • Integrates wireHTTP, route groups, auth, middleware, and fetch clients.
  • Triggers when code uses HTTP wiring functions or asks about APIs.
  • Delivers type-safe clients and organized route configurations.

SKILL.md

.github/skills/pikku-httpView on GitHub ↗
---
name: pikku-http
description: 'Use when adding HTTP routes, REST APIs, web endpoints, or SSE streams to a Pikku app. Covers wireHTTP, defineHTTPRoutes, route groups, auth, middleware, permissions, SSE, and generated fetch client.
TRIGGER when: code uses wireHTTP/defineHTTPRoutes/wireHTTPRoutes, user asks about REST endpoints, API routes, SSE, or the generated fetch client.
DO NOT TRIGGER when: user asks about WebSocket (use pikku-websocket), queue workers (use pikku-queue), or deployment (use pikku-deploy-*).'
---

# Pikku HTTP Wiring

Wire Pikku functions to HTTP endpoints. Supports single routes, composable route groups, auth, middleware, permissions, SSE, and auto-generated type-safe clients.

## Before You Start

Run these commands to understand the current project:

```bash
pikku info functions --verbose   # See existing functions, their types, tags, middleware
pikku info tags --verbose        # Understand project organization and naming conventions
pikku info middleware --verbose  # See what middleware is already applied
```

Follow existing patterns you find (naming, tag usage, file organization). See `pikku-concepts` for the core mental model.

## API Reference

### `wireHTTP(config)`

Wire a single function to an HTTP endpoint.

```typescript
import { wireHTTP } from '@pikku/core/http'

wireHTTP({
  method: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head',
  route: string,          // e.g. '/books/:bookId' — :params become data fields
  func: PikkuFunc,        // The function to call
  auth?: boolean,         // Override default auth (true = require session)
  tags?: string[],        // For grouping, middleware targeting
  permissions?: Record<string, PikkuPermission | PikkuPermission[]>,
  middleware?: PikkuMiddleware[],
  sse?: boolean,          // Enable Server-Sent Events
  contentType?: 'xml' | 'json',  // Response content type
  timeout?: number,       // Request timeout in ms
  headers?: HTTPHeadersSchema,   // Expected headers schema
  docs?: HTTPRouteDocsConfig,    // OpenAPI docs config
})
```

### `defineHTTPRoutes(config)` + `wireHTTPRoutes(config)`

Group routes with shared configuration. Groups are composable and nestable.

```typescript
import { defineHTTPRoutes, wireHTTPRoutes } from '.pikku/pikku-types.gen.js'

const routes = defineHTTPRoutes({
  basePath?: string,       // Prepended to all route paths
  tags?: string[],         // Applied to all routes in group
  auth?: boolean,          // Default auth for all routes (overridable per-route)
  middleware?: PikkuMiddleware[],
  routes: {
    [key: string]: {
      method: string,
      route: string,
      func: PikkuFunc,
      auth?: boolean,      // Override group auth
      permissions?: Record<string, PikkuPermission | PikkuPermission[]>,
      middleware?: PikkuMiddleware[],
    }
  }
})

wireHTTPRoutes({
  basePath?: string,       // Top-level prefix (e.g. '/api/v1')
  middleware?: PikkuMiddleware[],
  routes: {
    [key: string]: ReturnType<typeof defineHTTPRoutes>,
  }
})
```

Config cascading rules:

- `basePath` — concatenates down the chain
- `tags` — merge (union)
- `auth` — child overrides parent

### `addHTTPMiddleware(pattern, middlewares)`

```typescript
addHTTPMiddleware('*', [authBearer()]) // All routes
addHTTPMiddleware('/api/*', [rateLimit()]) // Pattern match
```

### `addHTTPPermission(pattern, permissions)`

```typescript
addHTTPPermission('/admin/*', { admin: [isAdmin] })
```

## Data Flow

Pikku merges route params, query params, and request body into a single `data` object:

```typescript
// POST /books/42?format=pdf  with body { title: "New Title" }
wireHTTP({ method: 'post', route: '/books/:bookId', func: updateBook })
// → updateBook receives: { bookId: "42", format: "pdf", title: "New Title" }
```

## Usage Patterns

### Single Route

```typescript
wireHTTP({
  method: 'get',
  route: '/books/:bookId',
  func: getBook,
})
```

### Route Groups (Recommended for CRUD)

```typescript
const booksRoutes = defineHTTPRoutes({
  tags: ['books'],
  routes: {
    list: { method: 'get', route: '/books', func: listBooks, auth: false },
    get: { method: 'get', route: '/books/:bookId', func: getBook },
    create: { method: 'post', route: '/books', func: createBook },
    delete: { method: 'delete', route: '/books/:bookId', func: deleteBook },
  },
})

const todosRoutes = defineHTTPRoutes({
  auth: false,
  tags: ['todos'],
  routes: {
    list: { method: 'get', route: '/todos', func: listTodos },
    create: { method: 'post', route: '/todos', func: createTodo },
    get: { method: 'get', route: '/todos/:id', func: getTodo },
  },
})

wireHTTPRoutes({
  basePath: '/api/v1',
  middleware: [cors()],
  routes: {
    books: booksRoutes,
    todos: todosRoutes,
  },
})
// Results in: GET /api/v1/books, POST /api/v1/books, etc.
```

### Auth & Permissions

```typescript
// Public route (no auth)
wireHTTP({ method: 'get', route: '/books', func: listBooks, auth: false })

// Route with permission check
wireHTTP({
  method: 'delete',
  route: '/books/:bookId',
  func: deleteBook,
  permissions: { admin: isAdmin },
})

// Pattern-based permissions
addHTTPPermission('/admin/*', { admin: isAdmin })
```

### Middleware

```typescript
import { cors, authBearer } from '@pikku/core/middleware'

// Global middleware
addHTTPMiddleware('*', [
  cors({ origin: 'https://app.example.com', credentials: true }),
  authBearer(),
])

// Scoped middleware
addHTTPMiddleware('/api/*', [rateLimit({ maxRequests: 100, windowMs: 60_000 })])

// Per-route middleware
wireHTTP({
  method: 'delete',
  route: '/books/:bookId',
  func: deleteBook,
  middleware: [auditLog],
})
```

### SSE (Server-Sent Events)

```typescript
wireHTTP({
  method: 'get',
  route: '/todos',
  func: getTodos,
  sse: true,
})

const getTodos = pikkuFunc({
  title: 'Get Todos',
  func: async ({ db, channel }, {}) => {
    const todos = await db.getTodos()

    if (channel) {
      for (const todo of todos) {
        channel.send({ todo })
        await sleep(100)
      }
      return
    }

    return { todos }
  },
})
```

### Generated Fetch Client

After `npx pikku prebuild`, a type-safe client is generated:

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

pikkuFetch.setServerUrl('http://localhost:4002')

const books = await pikkuFetch.get('/api/v1/books', {})
const book = await pikkuFetch.get('/api/v1/books/:bookId', { bookId: '42' })
const created = await pikkuFetch.post('/api/v1/books', {
  title: 'The Pikku Guide',
  author: 'You',
})

pikkuFetch.setAuthorizationJWT(token)
const deleted = await pikkuFetch.delete('/api/v1/books/:bookId', {
  bookId: created.bookId,
})
```

## Complete Example

```typescript
// functions/books.functions.ts
import { pikkuFunc, pikkuSessionlessFunc } from '#pikku'

export const listBooks = pikkuSessionlessFunc({
  title: 'List Books',
  func: async ({ db }, { limit }) => {
    return { books: await db.listBooks(limit) }
  },
})

export const getBook = pikkuFunc({
  title: 'Get Book',
  description: 'Retrieve a book by ID',
  func: async ({ db }, { bookId }) => {
    return await db.getBook(bookId)
  },
  permissions: { user: isAuthenticated },
})

export const createBook = pikkuFunc({
  title: 'Create Book',
  func: async ({ db }, { title, author }) => {
    return await db.createBook({ title, author })
  },
})

export const deleteBook = pikkuFunc({
  title: 'Delete Book',
  func: async ({ db }, { bookId }) => {
    await db.deleteBook(bookId)
    return { deleted: true }
  },
})

// wirings/books.http.ts
import { defineHTTPRoutes, wireHTTPRoutes } from '.pikku/pikku-types.gen.js'
import { addHTTPMiddleware } from '@pikku/core/http'
import { cors, authBearer } from '@pikku/core/middleware'

const booksRoutes = defineHTTPRoutes({
  tags: ['books'],
  routes: {
    list: { method: 'get', route: '/books', func: listBooks, auth: false },
    get: { method: 'get', route: '/books/:bookId', func: getBook },
    create: { method: 'post', route: '/books', func: createBook },
    delete: { method: 'delete', route: '/books/:bookId', func: deleteBook },
  },
})

wireHTTPRoutes({
  basePath: '/api',
  routes: { books: booksRoutes },
})

addHTTPMiddleware('*', [cors(), authBearer()])
```

More from pikkujs/pikku

SkillDescription
pikku-addon'Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon, addon(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, and cross-project function sharing.
pikku-ai-agent'Use when building AI agents, chatbots, or LLM-powered assistants with Pikku. Covers pikkuAIAgent, tool registration, memory, streaming, and agent invocation.
pikku-ai-vercel'Use when setting up AI agent execution with the Vercel AI SDK in a Pikku app. Covers VercelAIAgentRunner for streaming and non-streaming AI agent steps.
pikku-ai-voice'Use when adding voice input (speech-to-text) or voice output (text-to-speech) to AI agents in a Pikku app. Covers voiceInput/voiceOutput middleware hooks and STT/TTS service interfaces.
pikku-auth-js'Use when integrating Auth.js (NextAuth) with a Pikku app. Covers createAuthHandler, createAuthRoutes, and Auth.js configuration.
pikku-aws'Use when setting up AWS services (S3, SQS, Secrets Manager) in a Pikku app. Covers S3Content for file storage, SQSQueueService for queues, and AWSSecrets for secret management.
pikku-backblaze'Use when setting up Backblaze B2 file storage in a Pikku app. Covers B2Content for file uploads, downloads, and signed URLs.
pikku-cli'Use when building CLI commands with Pikku. Covers wireCLI, pikkuCLICommand, subcommands, options, parameters, custom renderers, and nested command groups.
pikku-concepts'Foundational guide to Pikku framework concepts. Use this skill when working with any Pikku codebase, starting a new Pikku project, or migrating a backend to Pikku. Covers the core mental model, function types, project structure, code generation, testing, and how Pikku maps to traditional backend patterns.
pikku-config'Use when managing secrets, environment variables, config, or OAuth2 credentials in a Pikku app. Covers wireSecret, wireVariable, wireOAuth2Credential, and typed config access.