Handlers
The handler() function from @venturekit/runtime is the primary API for writing route handlers. It wraps your business logic with authentication, body parsing, status code detection, middleware, and error handling.
Basic Usage
Section titled “Basic Usage”import { handler } from '@venturekit/runtime';
// Public endpoint (no scopes = no auth required)export const main = handler(async (_body, ctx, logger) => { logger.info('Health check'); return { status: 'healthy', timestamp: ctx.timestamp.toISOString() };});Authenticated Endpoints
Section titled “Authenticated Endpoints”Add scopes to require authentication and specific OAuth scope permissions:
export const main = handler(async (body, ctx, logger) => { logger.info('Creating task', { userId: ctx.user?.id }); return { id: '123', title: body.title };}, { scopes: ['tasks.write'] });When scopes are specified:
- The handler checks for a valid JWT in the request
- If no JWT is present, it returns
401 Unauthorized - If the JWT doesn’t contain all required scopes, it returns
403 Forbidden
Handler Signature
Section titled “Handler Signature”handler<TBody, TResult>( fn: (body: TBody, ctx: RequestContext, logger: Logger) => Promise<TResult>, config?: HandlerConfig)Parameters:
body— Parsed JSON request body (auto-parsed from the event)ctx— Request context with metadata, user info, and morelogger— Structured logger (Pino-based)
Type parameters:
TBody— Type of the request bodyTResult— Type of the return value
interface CreateTaskBody { title: string;}
interface Task { id: string; title: string;}
export const main = handler<CreateTaskBody, Task>( async (body, _ctx, logger) => { logger.info('Creating task', { title: body.title }); return { id: Date.now().toString(), title: body.title }; }, { scopes: ['tasks.write'] });Status Codes
Section titled “Status Codes”Status codes are auto-detected from the HTTP method:
| HTTP Method | Default Status |
|---|---|
GET | 200 OK |
PUT | 200 OK |
PATCH | 200 OK |
POST | 201 Created |
DELETE | 204 No Content |
Override explicitly:
export const main = handler(async (body, ctx, logger) => { return { accepted: true };}, { status: 200 }); // Force 200 instead of auto-detectedHandler Config
Section titled “Handler Config”interface HandlerConfig { scopes?: string[]; // Required OAuth scopes (empty = public) status?: 200 | 201 | 204; // Explicit status code middleware?: Middleware[]; // Custom middleware logLevel?: 'debug' | 'info' | 'warn' | 'error'; transactional?: boolean; // Wrap in a database transaction}Request Context
Section titled “Request Context”Every handler receives a RequestContext:
interface RequestContext { requestId: string; // Unique request ID for tracing timestamp: Date; // Request timestamp method: string; // HTTP method path: string; // Request path sourceIp: string; // Client IP userAgent: string; // User agent user: UserContext | null; // Authenticated user (or null) tenant: TenantContext | null;// Tenant (when @venturekit-pro/tenancy is used) locale: string; // Resolved locale queryParams?: Record<string, string | undefined>; tx?: unknown; // Database transaction (transactional handlers) intentOutputs?: Record<string, unknown>; // Infrastructure outputs rawEvent: APIGatewayProxyEventV2; // Raw AWS event}User Context
Section titled “User Context”When a request is authenticated:
interface UserContext { id: string; // Cognito sub email?: string; // User email scopes: string[]; // OAuth scopes claims: Record<string, unknown>; // All JWT claims}Transactional Handlers
Section titled “Transactional Handlers”Wrap your handler in a database transaction (requires @venturekit/data):
export const main = handler(async (body, ctx, logger) => { // ctx.tx is a transaction — auto-commits on success, rolls back on error await ctx.tx.query('INSERT INTO tasks (title) VALUES ($1)', [body.title]); await ctx.tx.query('UPDATE counters SET count = count + 1'); return { created: true };}, { scopes: ['tasks.write'], transactional: true });Custom Middleware
Section titled “Custom Middleware”Add middleware to individual handlers:
import { handler, corsMiddleware, timeoutMiddleware } from '@venturekit/runtime';
export const main = handler(async (_body, ctx, logger) => { return { ok: true };}, { middleware: [ timeoutMiddleware(5000), ],});See Middleware for details on built-in and custom middleware.