Skip to content

@venturekit/runtime API

Creates a unified Lambda handler that wraps your business logic with auth, body parsing, status codes, middleware, and error handling.

function handler<TBody = unknown, TResult = unknown>(
fn: HandlerFn<TBody, TResult>,
config?: HandlerConfig
): (event: APIGatewayProxyEventV2, context: Context) => Promise<APIGatewayProxyResultV2>

Creates a handler for non-HTTP Lambda functions (standalone functions, queue consumers, scheduled tasks). Provides middleware, logging, validation, transactions, timeout, and application-level retries.

function taskHandler<TEvent = unknown, TResult = unknown>(
fn: TaskHandlerFn<TEvent, TResult>,
config?: TaskHandlerConfig<TEvent>
): (event: TEvent, context: Context) => Promise<TResult>

Builds a RequestContext from an API Gateway event.

function buildContext(
event: APIGatewayProxyEventV2,
options: { supportedLocales: string[]; defaultLocale: string }
): RequestContext

Extracts user information from JWT claims in the API Gateway event.

function extractUserContext(event: APIGatewayProxyEventV2): UserContext | null

extractLocale(event, supportedLocales, defaultLocale)

Section titled “extractLocale(event, supportedLocales, defaultLocale)”

Extracts the locale from the Accept-Language header.

function extractLocale(event: APIGatewayProxyEventV2, supported: string[], fallback: string): string
FunctionStatusDescription
success(data, meta?)200Success response
created(data, meta?)201Created response
noContent(meta?)204No content response
error(statusCode, body)*Custom error response
errorResponse(error, meta?)*Format a VentureError
redirect(url, statusCode?)301/302Redirect response
FunctionDescription
compose(middlewares)Compose middleware into a single function
loggingMiddleware(logger)Request/response logging with timing
corsMiddleware(options)CORS headers and preflight handling
timeoutMiddleware(ms)Request timeout enforcement
errorBoundaryMiddleware(handler)Catch and format errors
rateLimitMiddleware(options)Rate limiting with pluggable stores
requestIdMiddleware()Ensure request ID is set
timingMiddleware()Add X-Response-Time header
apiKeyAuthMiddleware(options)API key authentication
ExportDescription
LoggerLogger class
createLogger(config?)Create a new logger instance
loggerDefault logger instance
type HandlerFn<TBody, TResult> = (
body: TBody,
ctx: RequestContext,
logger: Logger
) => Promise<TResult>
interface HandlerConfig<TBody = unknown, TQuery = unknown, TParams = unknown> {
scopes?: string[]
status?: 200 | 201 | 204
middleware?: Middleware[]
logLevel?: 'debug' | 'info' | 'warn' | 'error'
transactional?: boolean
body?: ZodLikeSchema<TBody> // Zod schema for request body validation
query?: ZodLikeSchema<TQuery> // Zod schema for query parameter validation
params?: ZodLikeSchema<TParams> // Zod schema for path parameter validation
}

When a Zod schema is provided, the input is validated before the handler runs. On failure, a 422 ValidationError is thrown with structured field errors.

interface RequestContext {
requestId: string
timestamp: Date
method: string
path: string
sourceIp: string
userAgent: string
user: UserContext | null
tenant: TenantContext | null
locale: string
queryParams?: Record<string, string | undefined>
tx?: unknown
intentOutputs?: Record<string, unknown>
isInternal: boolean
invokeId?: string
rawEvent: APIGatewayProxyEventV2
}
  • isInternaltrue when this request came from another VentureKit function via invoke(), false for external API Gateway requests. Use this to skip rate limiting, auth, or other external-only logic.
  • invokeId — The unique invoke ID for internal calls, useful for tracing function-to-function calls.
interface UserContext {
id: string
email?: string
scopes: string[]
claims: Record<string, unknown>
}
interface TenantContext {
id: string
slug?: string
metadata: Record<string, unknown>
}

Generic middleware type that works with both HTTP handlers and task handlers. Middleware written against BaseContext is reusable across all handler types.

interface Middleware<TCtx extends BaseContext = BaseContext> {
name: string
fn: MiddlewareFn<TCtx>
}
type MiddlewareFn<TCtx extends BaseContext = BaseContext> = (
ctx: TCtx,
next: () => Promise<any>
) => Promise<any>
// This middleware works in both handler() and taskHandler()
const timing: Middleware = {
name: 'timing',
fn: async (ctx, next) => {
const start = Date.now();
const result = await next();
console.log(`${ctx.requestId} took ${Date.now() - start}ms`);
return result;
},
};

Shared fields available in all handler types:

interface BaseContext {
requestId: string
timestamp: Date
tx?: Transaction
intentOutputs?: Record<string, unknown>
traceId?: string
}

RequestContext extends BaseContext with HTTP-specific fields. TaskContext extends BaseContext with Lambda-specific fields (functionName, event, logger).

interface TaskContext extends BaseContext {
functionName: string
getRemainingTimeInMillis: () => number
rawContext: Context
event: unknown
logger: Logger
}
interface TaskHandlerConfig<TEvent = unknown> {
middleware?: Middleware<TaskContext>[]
logLevel?: 'debug' | 'info' | 'warn' | 'error'
transactional?: boolean
event?: ZodLikeSchema<TEvent> // Zod schema for event validation
timeout?: number // Timeout in ms (default: none, Lambda timeout applies)
retries?: number // Application-level retries within one invocation (default: 0)
retryDelayMs?: number // Initial retry delay in ms, doubles each attempt (default: 1000)
}

Thrown when event validation fails. Never retried by the application retry loop.

class TaskValidationError extends Error {
readonly issues: Array<{ path: (string | number)[]; message: string }>
}
ClassStatusCode
VentureError500Base class
BadRequestError400BAD_REQUEST
UnauthorizedError401UNAUTHORIZED
ForbiddenError403FORBIDDEN
NotFoundError404NOT_FOUND
ConflictError409CONFLICT
PayloadTooLargeError413PAYLOAD_TOO_LARGE
ValidationError422VALIDATION_ERROR
RateLimitError429RATE_LIMITED
InternalError500INTERNAL_ERROR
ServiceUnavailableError503SERVICE_UNAVAILABLE

Type guard to check if an error is a VentureError.

function isVentureError(error: unknown): error is VentureError

Unified API for calling other VentureKit functions. Works locally and in production.

// Route invocation
const users = await invoke<User[]>('users/list', { method: 'GET' });
// Function invocation
const result = await invoke({ function: 'process-orders' }, {
payload: { orderId: 'abc' },
});
// Fire-and-forget
await invoke('notifications/send', { mode: 'async', body: { userId: '42' } });

Auto-propagation: invoke() automatically propagates trace context, tenant ID, and user ID. You can override any of these explicitly.

OptionDescription
methodHTTP method (default: POST)
bodyRequest body
pathParamsPath parameters
queryParamsQuery parameters
headersAdditional headers
mode'sync' (default) or 'async'
traceTrace context (auto-propagated from headers)
tenantIdTenant ID (auto-propagated from context)
userIdUser ID (auto-propagated from context)
fallbackDefault result when the target is unavailable (enables circuit breaker)

Add a single fallback param to protect against cascading failures. VentureKit automatically tracks errors per target — after 5 consecutive failures the circuit “opens” and returns your fallback instantly, without calling the target. After 30 seconds it probes once to see if the target recovered.

// Route call with fallback — one extra param, that's it
const users = await invoke<User[]>('users/list', {
method: 'GET',
fallback: { statusCode: 503, data: [], headers: {} },
});
// Function call with fallback
const result = await invoke(
{ function: 'process-orders', project: 'billing' },
{ payload: { orderId: '123' }, fallback: { statusCode: 503, data: null, headers: {} } },
);

No setup required — the circuit breaker is built-in. If you need custom thresholds:

import { configureCircuitBreaker } from '@venturekit/runtime/patterns';
// Optional: customize globally (call once at startup)
configureCircuitBreaker({
failureThreshold: 3, // open after 3 failures (default: 5)
resetTimeoutMs: 60_000, // probe after 60s (default: 30s)
});

You can also use createCircuitBreaker() directly for advanced use cases outside of invoke().

Call functions in other VentureKit projects (microservices):

// Call 'send-invoice' in the 'billing' project
const result = await invoke(
{ function: 'send-invoice', project: 'billing' },
{ payload: { orderId: '123' } },
);

The resolved Lambda name becomes {project}-{stage}-fn-{name}.

For local development, set VENTURE_LOCAL_URLS so each project’s dev server is reachable:

Terminal window
VENTURE_LOCAL_URLS="app=http://localhost:3000,billing=http://localhost:3001"

Creates a LambdaInvoker that routes calls to the local dev server via HTTP. Used automatically when VENTURE_LOCAL=true.

Supports cross-project routing via VENTURE_LOCAL_URLS — matches the function name prefix against the registry and routes to the correct project’s dev server.

Import from the /ws subpath:

import { connectionStore } from '@venturekit/runtime/ws';
MethodDescription
save(connectionId)Save a new connection (unauthenticated)
authenticate(connectionId, metadata)Upgrade to authenticated
remove(connectionId)Remove a connection
get(connectionId)Get a connection record
getAll()Get every authenticated connection (prefer getByUser / getByTenant at scale)
getByUser(userId)Get all connections for a user
getByTenant(tenantId)Get all connections for a tenant
postToConnection(domain, stage, connId, data)Send to one connection
sendToUser(domain, stage, userId, data)Send to all user sessions
sendToTenant(domain, stage, tenantId, data)Send to all tenant connections
broadcast(domain, stage, data)Send to all connections