Sessions
Read, write, clear. That's the API.
Whether the request arrives over HTTP, WebSocket, or CLI — your function reads and writes the session the same way.
Middleware loads
Session populated from cookie, bearer token, or connection state
Function reads
Access session via the wire parameter — userId, role, etc.
Function modifies
Call setSession() or clearSession() to update
Middleware persists
Changes saved back to the transport — cookie, store, etc.
export const login = pikkuFunc({
auth: false,
func: async (
{ jwt, db },
{ email, password },
{ setSession }
) => {
const user = await db.verifyCredentials(email, password)
// Works the same whether it's an HTTP cookie,
// WebSocket connection, or CLI token
setSession({ userId: user.id, role: user.role })
return { token: jwt.sign({ userId: user.id }) }
}
})
export const getProfile = pikkuFunc({
func: async ({ db }, _data, { session }) => {
// session is loaded by middleware before
// your function runs — same API everywhere
return await db.getUser(session.userId)
}
})
export const logout = pikkuFunc({
func: async ({}, _data, { clearSession }) => {
clearSession()
}
})
Permissions
Boolean checks. Composable logic.
Permissions run before your function. Return true to allow, false to reject. Group them with OR/AND logic.
import { pikkuAuth } from '#pikku'
// Session-only checks — receives (services, session)
// Use for authentication gates, role checks, MCP tools, AI agents
export const isAuthenticated = pikkuAuth(
async (_services, session) => !!session
)
export const isAdmin = pikkuAuth(
async (_services, session) => session?.role === 'admin'
)
import { pikkuPermission } from '#pikku'
// Data-aware checks — receives (services, data, wire)
// Use when authorization depends on the actual request data
export const isOwner = pikkuPermission(
async ({ db }, { bookId }, { session }) => {
const book = await db.getBook(bookId)
return book?.authorId === session?.userId
}
)
export const hasBookAccess = pikkuPermission(
async ({ db }, { bookId }, { session }) => {
return await db.hasAccess(session?.userId, bookId)
}
)
export const deleteBook = pikkuFunc({
func: async ({ db }, { bookId }) => {
await db.deleteBook(bookId)
},
// OR logic across keys, AND within arrays
permissions: {
admin: isAdmin, // OR: admins can delete
owner: isOwner, // OR: book author can delete
reviewer: [isAuthenticated, hasBookAccess] // AND: both must pass
}
})
pikkuAuth
Session-only — receives (services, session). Use for authentication gates, role checks, MCP tools, and AI agents.
pikkuPermission
Data-aware — receives (services, data, wire). Use when authorization depends on request data, e.g. ownership or access checks.
OR / AND composition
Each key in the permissions object is an OR group. Wrap in an array for AND logic. If any group passes, the request proceeds.
Auth Middleware
Built-in strategies. Four scopes.
Bearer tokens, cookies, and API keys ship out of the box. Apply middleware globally, by route prefix, by tag, or per-wiring.
authBearer
JWT from the Authorization header. Decodes and sets session automatically.
authCookie
JWT-encoded cookie. Auto-refreshes on session change. Configurable expiry and options.
authAPIKey
Reads x-api-key header or apiKey query param. Decodes as JWT to set session.
import { authBearer, authCookie, authAPIKey } from '@pikku/core/middleware'
import { addHTTPMiddleware, addHTTPPermission } from '#pikku'
// JWT bearer token — reads Authorization header
addHTTPMiddleware([authBearer()])
// Cookie-based sessions — auto-refreshes JWT
addHTTPMiddleware([
authCookie({
name: 'session',
expiresIn: { value: 30, unit: 'day' },
options: { httpOnly: true, secure: true },
})
])
// API key — from x-api-key header or ?apiKey= query
addHTTPMiddleware([authAPIKey({ source: 'all' })])
// Global: all HTTP routes
addHTTPMiddleware('*', [authBearer()])
// Prefix-based: only /admin/* routes
addHTTPMiddleware('/admin/*', [auditLog])
addHTTPPermission('/admin/*', { admin: requireAdmin })
// Tag-based: applies to any wiring with 'api' tag
addMiddleware('api', [rateLimiter])
addPermission('api', { auth: requireAuth })
// Inline: per-wiring
wireHTTP({
route: '/books/:id',
func: getBook,
middleware: [cacheControl],
permissions: { owner: requireOwnership },
})
Global
Applies to all wirings of a type
Prefix
Route-pattern matching like /admin/*
Tag
Any wiring tagged with a keyword
Inline
Directly on a single wiring
Secure by default
Scaffold a project with auth middleware pre-configured. Sessions work across every protocol from day one.
MIT Licensed · Works with Express, Fastify, Lambda & Cloudflare