@venturekit/runtime API
Functions
Section titled “Functions”handler(fn, config?)
Section titled “handler(fn, config?)”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>createHandler(defaults)
Section titled “createHandler(defaults)”Returns a drop-in replacement for handler with app-wide defaults baked in — so every route stops repeating the same middleware stack (tenant resolution, session auth, per-tenant scope grants). Define it once and import the bound handler across your routes.
function createHandler(defaults: HandlerDefaults): BoundHandler
interface HandlerDefaults { middleware?: Middleware<RequestContext>[] // prepended to each route's own middleware logLevel?: 'debug' | 'info' | 'warn' | 'error' // fallback when the route sets none}import { createHandler, setDefaultAuthMiddleware } from '@venturekit/runtime';
setDefaultAuthMiddleware(cookieAuthMiddleware()); // scoped routesexport const apiHandler = createHandler({ middleware: [tenancyMiddleware, memberScopesMiddleware()],});
// src/routes/....ts — routes shrink to their essenceexport const main = apiHandler(async (_body, ctx) => { /* … */ }, { scopes: ['admin.members.read'],});Merge semantics: middleware runs defaults-first ([...defaults, ...route]); the default auth middleware still runs before both; logLevel — the route value wins; everything else is per-route. Routes that must NOT inherit the defaults (health checks, cross-tenant/platform endpoints, webhook receivers) keep using the raw handler.
setDefaultAuthMiddleware(mw)
Section titled “setDefaultAuthMiddleware(mw)”Overrides the middleware the runtime injects to populate ctx.user on scoped routes. By default the runtime lazily imports @venturekit/auth/server and uses its cookieAuthMiddleware(); if that package isn’t installed it falls back to the API Gateway Cognito Authorizer (claims in event.requestContext.authorizer.jwt.claims). Pass null to disable auth-middleware injection entirely.
function setDefaultAuthMiddleware(mw: Middleware<RequestContext> | null): voidfunction _resetDefaultAuthMiddlewareForTesting(): void // test-only: clears the resolution cacheTiming: each handler() / createHandler() factory caches the resolved auth middleware on its first request — call this in app bootstrap (or before invoking test handlers) so it takes effect.
taskHandler(fn, config?)
Section titled “taskHandler(fn, config?)”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>buildContext(event, options)
Section titled “buildContext(event, options)”Builds a RequestContext from an API Gateway event.
function buildContext( event: APIGatewayProxyEventV2, options: { supportedLocales: string[]; defaultLocale: string }): RequestContextextractUserContext(event)
Section titled “extractUserContext(event)”Extracts user information from JWT claims in the API Gateway event.
function extractUserContext(event: APIGatewayProxyEventV2): UserContext | nullextractLocale(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): stringgetRequestHost(source, options?)
Section titled “getRequestHost(source, options?)”Returns the browser-facing host of a request — what the address bar shows — for cookie Domain decisions and redirect URLs. Accepts a RequestContext (reads ctx.rawEvent) or a raw API Gateway event.
function getRequestHost( source: RequestContext | { headers?: Record<string, string | undefined> } | null | undefined, options?: { allowedHosts?: readonly string[] | ((host: string) => boolean) },): string
function isAllowedHost(host: string, allowedHosts: readonly string[] | ((host: string) => boolean)): booleanHeader priority is origin → referer → host → x-forwarded-host (origin/referer outrank host so a same-origin SPA proxy resolves to the browser’s host, not the API’s). Full-URL values are reduced to their host (https://app.example.com/login → app.example.com, port kept); bare host values (app.example.com:3000) pass through as-is. Returns '' when no usable header is present (the opaque Origin: null is skipped).
Trust boundary. All four headers are chosen by the caller. The bare result is safe for a cookie Domain (a browser drops a cookie whose Domain does not cover the current host) and for logging — it is not safe for a redirect Location or an absolute URL in an e-mail, where an unvalidated host is an open redirector. Pass allowedHosts for those: exact host[:port] entries ('app.example.com', 'localhost:3000'), dot-prefixed apexes that match the apex and every subdomain ('.example.com'), or a predicate. Candidates that fail the allow-list are skipped and '' is returned when none passes — treat that as “use the canonical host”, never as “fall back to Host”. isAllowedHost is the same matcher, exported for hosts that arrive by other routes (a ?next= parameter).
setCookie(ctx, value)
Section titled “setCookie(ctx, value)”Appends a fully-formed Set-Cookie header value to the outgoing response. The runtime drains ctx.cookies into the API Gateway HTTP API v2 cookies field — one Set-Cookie per entry. Formatting is the caller’s job; use @venturekit/auth/server’s buildSessionCookies / buildClearSessionCookies / buildOAuthStateCookie to produce well-formed strings.
function setCookie(ctx: BaseContext, value: string): voidResponse Helpers
Section titled “Response Helpers”| Function | Status | Description |
|---|---|---|
success(data, meta?) | 200 | Success response |
created(data, meta?) | 201 | Created response |
noContent(meta?) | 204 | No content response |
error(statusCode, body) | * | Custom error response |
errorResponse(error, meta?) | * | Format a VentureError |
redirect(url, statusCode?) | 301/302 | Redirect response |
By default handler() wraps whatever a route returns in the JSON success envelope ({ data, meta }). For browser-navigation endpoints that need full control of status/headers/body — OAuth redirects, session handoffs, HTML — return a pre-built result through rawResult() so the runtime passes it through untouched (cookies set via setCookie are still attached):
function rawResult(result: APIGatewayProxyResultV2): RawResult // mark for pass-throughfunction isRawResult(value: unknown): value is RawResult // detect a wrapped result
export const main = handler(async (_body, ctx) => { setCookie(ctx, sessionCookie); return rawResult(redirect('/dashboard'));});Middleware
Section titled “Middleware”| Function | Description |
|---|---|
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 |
Logger
Section titled “Logger”| Export | Description |
|---|---|
Logger | Logger class |
createLogger(config?) | Create a new logger instance |
logger | Default logger instance |
HandlerFn<TBody, TResult>
Section titled “HandlerFn<TBody, TResult>”type HandlerFn<TBody, TResult> = ( body: TBody, ctx: RequestContext, logger: Logger) => Promise<TResult>HandlerConfig
Section titled “HandlerConfig”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.
RequestContext
Section titled “RequestContext”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> pathParams?: Record<string, string | undefined> tx?: unknown intentOutputs?: Record<string, unknown> isInternal: boolean invokeId?: string cookies: string[] rawEvent: APIGatewayProxyEventV2}isInternal—truewhen this request came from another VentureKit function viainvoke(),falsefor 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.pathParams— Route[param]segments (e.g.{ id: '123' }forGET /users/{id}), overwritten with the parsed value when aparamsschema is supplied. Read asctx.pathParams?.['id'].cookies— OutgoingSet-Cookievalues; append viasetCookie(ctx, …). Drained into the HTTP API v2 response by the runtime.
UserContext
Section titled “UserContext”interface UserContext { id: string email?: string scopes: string[] claims: Record<string, unknown>}TenantContext
Section titled “TenantContext”interface TenantContext { id: string slug?: string metadata: Record<string, unknown>}Middleware<TCtx>
Section titled “Middleware<TCtx>”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>Universal middleware example
Section titled “Universal middleware example”// 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; },};BaseContext
Section titled “BaseContext”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).
TaskContext
Section titled “TaskContext”interface TaskContext extends BaseContext { functionName: string getRemainingTimeInMillis: () => number rawContext: Context event: unknown logger: Logger}TaskHandlerConfig<TEvent>
Section titled “TaskHandlerConfig<TEvent>”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)}TaskValidationError
Section titled “TaskValidationError”Thrown when event validation fails. Never retried by the application retry loop.
class TaskValidationError extends Error { readonly issues: Array<{ path: (string | number)[]; message: string }>}Error Classes
Section titled “Error Classes”| Class | Status | Code |
|---|---|---|
VentureError | 500 | Base class |
BadRequestError | 400 | BAD_REQUEST |
UnauthorizedError | 401 | UNAUTHORIZED |
ForbiddenError | 403 | FORBIDDEN |
NotFoundError | 404 | NOT_FOUND |
ConflictError | 409 | CONFLICT |
PayloadTooLargeError | 413 | PAYLOAD_TOO_LARGE |
ValidationError | 422 | VALIDATION_ERROR |
RateLimitError | 429 | RATE_LIMITED |
InternalError | 500 | INTERNAL_ERROR |
ServiceUnavailableError | 503 | SERVICE_UNAVAILABLE |
isVentureError(error)
Section titled “isVentureError(error)”Type guard to check if an error is a VentureError.
function isVentureError(error: unknown): error is VentureErrorconst VENTURE_ERROR_MARKER: unique symbol // Symbol.for('venturekit.error')Besides instanceof VentureError, the guard accepts any object carrying [VENTURE_ERROR_MARKER]: true with a string code, a statusCode in 400–599 and a string message. That lets a package that does not depend on the runtime — @venturekit-pro/billing’s PlanUpgradeRequiredError (402) and PlanLimitReachedError (403) are the shipped examples — raise errors the handler’s boundary renders with their intended status instead of a 500. To mark your own: Object.defineProperty(err, Symbol.for('venturekit.error'), { value: true }), or extend VentureError when the runtime is already a dependency.
createHealthHandler(options?)
Section titled “createHealthHandler(options?)”A ready-made Lambda handler for GET /health: runs the named dependency probes concurrently, answers 200 when every required one passes and 503 otherwise, always with Cache-Control: no-store so no proxy serves a stale “ok”. Wire it as a route’s main export directly — it is not wrapped in handler(), so no auth middleware, body parsing or defaults apply.
function createHealthHandler(options?: HealthHandlerOptions): (event?: APIGatewayProxyEventV2) => Promise<APIGatewayProxyResultV2>function runHealthChecks(options?: HealthHandlerOptions): Promise<HealthReport> // the report without the HTTP envelope
interface HealthHandlerOptions { checks?: Record<string, () => Promise<unknown> | unknown>; // empty = liveness only timeoutMs?: number; // per check; default 2000 — a probe must not hang optional?: string[]; // failures reported but never a 503 (a cache, an optional integration) info?: Record<string, string>; // static fields merged into the report: version, commit, region exposeErrors?: boolean; // include each failure message; default only under VENTURE_LOCAL=true or VK_EXPOSE_ERROR_DETAIL=true}import { createHealthHandler } from '@venturekit/runtime';import { query } from '@venturekit/data';
export const main = createHealthHandler({ checks: { database: () => query('SELECT 1'), cache: () => cache.ping(), }, optional: ['cache'], info: { version: process.env.APP_VERSION ?? 'dev' },});The report is { status: 'ok' | 'degraded', checks: { [name]: { status, durationMs, optional?, error? } }, timestamp, ...info }. Error text is withheld from a public probe by default — ECONNREFUSED 10.0.3.12:5432 is topology you do not want to publish.
Lambda-to-Lambda Invocation
Section titled “Lambda-to-Lambda Invocation”Import from the /patterns subpath — invoke, saga, parallel, the idempotency middleware and the circuit breaker are not re-exported from the package root:
import { invoke, saga, parallel, idempotencyMiddleware } from '@venturekit/runtime/patterns';invoke(target, options?)
Section titled “invoke(target, options?)”Unified API for calling other VentureKit functions. Works locally and in production.
// Route invocationconst users = await invoke<User[]>('users/list', { method: 'GET' });
// Function invocationconst result = await invoke({ function: 'process-orders' }, { payload: { orderId: 'abc' },});
// Fire-and-forgetawait 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.
Internal-call signing. Because every handler sits behind API Gateway, a plain x-venturekit-source: internal header would be forgeable; invoke() therefore signs ${functionName}:${timestamp} with HMAC-SHA256 and the receiver verifies it before setting ctx.isInternal (5-minute replay window, VENTURE_INTERNAL_HMAC_TTL_MS). The key is resolved from VENTURE_INTERNAL_HMAC_SECRET (inline — .env files, tests) or, as vk deploy wires it, from the Secrets Manager ARN in VENTURE_INTERNAL_HMAC_SECRET_ARN, fetched once per container and cached in memory so it never sits in a function’s environment block. Inside Lambda, verification fails closed: with no key configured nothing is internal, rather than everything being able to claim it; the public local-dev fallback only applies under vk dev.
| Option | Description |
|---|---|
method | HTTP method (default: POST) |
body | Request body |
pathParams | Path parameters |
queryParams | Query parameters |
headers | Additional headers |
mode | 'sync' (default) or 'async' |
trace | Trace context (auto-propagated from headers) |
tenantId | Tenant ID (auto-propagated from context) |
userId | User ID (auto-propagated from context) |
fallback | Default result when the target is unavailable (enables circuit breaker) |
Resilient invocation (circuit breaker)
Section titled “Resilient invocation (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 itconst users = await invoke<User[]>('users/list', { method: 'GET', fallback: { statusCode: 503, data: [], headers: {} },});
// Function call with fallbackconst 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().
Cross-project invocation
Section titled “Cross-project invocation”Call functions in other VentureKit projects (microservices):
// Call 'send-invoice' in the 'billing' projectconst 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:
VENTURE_LOCAL_URLS="app=http://localhost:3000,billing=http://localhost:3001"createLocalInvoker()
Section titled “createLocalInvoker()”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.
Lower-level helpers
Section titled “Lower-level helpers”| Function | Description |
|---|---|
invokeDirect(functionName, payload?, { mode? }) | Invoke a Lambda by its physical name, bypassing route/function resolution; returns DirectInvokeResult<T> |
resolveFunctionName(name, project?) | {project}-{stage}-fn-{name} — the name invoke({ function }) targets |
Idempotency helpers
Section titled “Idempotency helpers”| Export | Description |
|---|---|
idempotencyMiddleware(options) | Dedupe requests by an idempotency key; stores go in @venturekit/data/idempotency (Postgres) or createMemoryIdempotencyStore() (tests) |
sqsMessageIdExtractor() | A TaskIdempotencyKeyExtractor using the SQS messageId — retries after a visibility timeout reuse it, so it is a natural key for queue handlers |
IdempotencyConflictError | A request with the same key is already in progress |
MissingIdempotencyKeyError | The key was required but absent |
createMemorySagaStore() is the in-memory SagaStore counterpart for saga() in tests. See the Idempotency and Saga guides.
Domain Events (/patterns)
Section titled “Domain Events (/patterns)”The publish side of the events intent: a type-safe publisher for an EventBridge custom bus that fans out over Redis pub/sub under vk dev. Trace context is baked into the event detail, so subscriber Lambdas read it via extractTraceContext exactly as they do for queue messages.
import { publishEvent, publishEvents } from '@venturekit/runtime/patterns';
await publishEvent('tenant.created', { tenantId: 'tnt_123', slug: 'acme', plan: 'pro' });
// Target a specific bus and override the sourceawait publishEvent('plan.changed', payload, { busId: 'domain', source: 'admin-api' });
// Batch — chunked into PutEvents calls of 10await publishEvents([ { detailType: 'order.placed', detail: order }, { detailType: 'stock.reserved', detail: reservation, options: { busId: 'inventory' } },]);| Export | Description |
|---|---|
publishEvent(detailType, detail, options?) | Publish one event; returns { eventId }. Throws when EventBridge reports a failed entry |
publishEvents(events, shared?) | Publish many EventInputs ({ detailType, detail, options? }), chunked automatically |
PublishEventOptions | busId (an events[] id → EVENT_BUS_<ID>_NAME; default VENTURE_EVENT_BUS_NAME), source (default VENTURE_EVENT_SOURCE → VENTURE_PROJECT_NAME → 'app'), resources, trace |
buildEventEntry(detailType, detail, options?) | The pure PutEvents entry (EventBusName, Source, DetailType, Detail, …) — for testing envelope shape without AWS |
subscribeLocalBus(onEvent) / localBusChannel(busName) / closeLocalEventPublisher() | Local-dev transport: subscribe to the Redis fan-out (vk dev’s router uses this), the channel name for a bus, and shutdown |
Transactional Outbox (/patterns and /patterns/outbox)
Section titled “Transactional Outbox (/patterns and /patterns/outbox)”publishEvent() puts an event on the bus now — the wrong guarantee for a state change, because the row commit and the publish are separate calls. The outbox pattern makes the intent part of the transaction: the event is inserted alongside the state change (appendToOutbox from @venturekit/data/outbox) and a relay publishes it afterwards, in order per aggregate.
The @venturekit/runtime/patterns/outbox subpath imports nothing, so a package of event definitions (aggregate names, payload types) can be shared with a browser bundle; only createEventBridgeEventBus (from /patterns) pulls in a transport.
// In the request, inside the transactionimport { createEvent } from '@venturekit/runtime/patterns/outbox';import { appendToOutbox } from '@venturekit/data/outbox';
const event = createEvent({ type: 'order.placed', aggregateType: 'order', aggregateId: order.id, payload: { total: order.total }, actor: { type: 'user', id: ctx.user!.id }, tenantId: ctx.tenant?.id,});await appendToOutbox(ctx.tx.query, event);
// In a `schedules` cron — src/crons/outbox-relay.tsimport { relayOnce, createEventBridgeEventBus } from '@venturekit/runtime/patterns';import { createPostgresOutboxStore } from '@venturekit/data/outbox';
const result = await relayOnce(createPostgresOutboxStore(), createEventBridgeEventBus(), { limit: 100, maxAttempts: 5 });// → { published, failed, deferred, quarantined }| Export | Description |
|---|---|
createEvent(input) | Build a DomainEvent — id (newEventId()), type, version (1), aggregateType, aggregateId, tenantId, occurredAt, actor, payload, correlationId?, causationId? |
newEventId() | Event id generator |
relayOnce(store, bus, options?) | Claim up to limit (100) rows, publish in aggregate order, defer later events of an aggregate whose earlier event failed in this pass, and quarantine rows that reached maxAttempts (5) for an operator. Returns RelayResult — { published, failed, deferred, quarantined } |
toPublishedEvent(row) | Map an OutboxRow back to a PublishedEvent |
createEventBridgeEventBus(options?) | An EventBus whose publish() calls publishEvent(event.type, event, options) |
EventBus / OutboxStore | The two ports — publish(event); claim(limit, maxAttempts), markPublished(ids), recordFailures(failures) — implement them to relay through another transport or store |
DomainEvent, PublishedEvent, EventActor, EventMeta, OutboxRow, OutboxFailure, RelayOptions, RelayResult | Envelope and result types |
Secrets (/secrets)
Section titled “Secrets (/secrets)”import { createSecretsClient } from '@venturekit/runtime/secrets';
const secrets = createSecretsClient({ defaultStore: 'secretsmanager', cacheTtlMs: 300_000 });const apiKey = await secrets.require('stripe-key'); // throws when missingconst maybe = await secrets.get('optional-flag'); // undefined when missingsecrets.invalidate('stripe-key'); // or invalidate() for everything| Export | Description |
|---|---|
createSecretsClient(options?) | Cached client over a SecretProvider — defaultStore ('ssm' | 'secretsmanager'), cacheTtlMs, provider, region |
createSsmProvider(region?) / createSecretsManagerProvider(region?) | Providers for SSM Parameter Store and Secrets Manager |
createMemorySecretProvider(initial?) | In-memory provider for tests and vk dev, with set(name, value) / delete(name) |
SecretsClient, SecretProvider, SecretStore, SecretsClientOptions | Types |
Pairs with the secrets intent, which provisions the entries.
Rate limiting (/rate-limit)
Section titled “Rate limiting (/rate-limit)”The root rateLimitMiddleware(options) needs a store. Presets and stores live on the /rate-limit subpath:
| Export | Description |
|---|---|
RATE_LIMIT_PRESETS | relaxed (200/min), standard (100/min), strict (20/min) — RateLimitPreset / RateLimitPresetConfig |
createMemoryRateLimitStore(options?) | Per-container in-memory store (limit, windowMs) — fine for vk dev, not shared across Lambdas |
createDynamoDBRateLimitStore(options?) | Shared store over the table named by options.tableName or VENTURE_RATE_LIMIT_TABLE (DynamoDBRateLimitStoreOptions) |
createDefaultRateLimiter(options?) | Picks memory locally and DynamoDB on Lambda (DefaultRateLimiterOptions) |
OpenAPI (/openapi)
Section titled “OpenAPI (/openapi)”What vk generate openapi uses; available programmatically:
| Export | Description |
|---|---|
generateOpenAPISpec(routes, options) | Build an OpenAPIDocument from RouteDefinition[] (OpenAPIGeneratorOptions: title, version, servers, …) |
serializeOpenAPI(doc, pretty?) / serializeOpenAPIYaml(doc) | JSON / YAML output |
filePathToOpenAPI(filePath) | src/routes/tasks/[id]/get.ts → /tasks/{id} |
inferTag(path) | First path segment as the operation tag |
OpenAPIDocument, OpenAPIInfo, OpenAPIServer, RouteDefinition, JsonSchema, HttpMethod | Types |
Testing utilities (/testing)
Section titled “Testing utilities (/testing)”import { createTestHandler, mockEvent, mockContext, mockLogger } from '@venturekit/runtime/testing';
const call = createTestHandler(main); // wraps a Lambda handlerconst res = await call({ method: 'POST', body: { title: 'x' }, claims: { sub: 'u1', scope: 'tasks.write' } });| Export | Description |
|---|---|
mockEvent(options?) | An APIGatewayProxyEventV2 (method, path, body, headers, queryStringParameters, pathParameters, sourceIp, userAgent, claims for the authorizer JWT) |
mockContext(options?) | A RequestContext with optional user / tenant (null for anonymous), queryParams, locale, isInternal, … |
mockLambdaContext() | A Lambda Context |
mockLogger() | A Logger that captures entries in .logs (CapturedLog[]) with .clear() |
createTestHandler(lambdaHandler) | Returns (options?: TestInvokeOptions) => Promise<APIGatewayProxyResultV2> |
Queue driver (/queue)
Section titled “Queue driver (/queue)”queueBatchHandler consumers and the queues intent work without touching this subpath. It exposes the driver contract for cross-process queues in dev and for custom transports:
| Export | Description |
|---|---|
createRedisDriver(config) | Redis-backed QueueDriver used by vk dev (QueueConfig) |
QueueDriver | sendMessage, sendMessages, receiveMessage, deleteMessage, changeMessageVisibility, purgeQueue, getQueueStats, close |
QueueMessage, QueueStats, SendMessageOptions, SendMessageResult, ReceiveMessageOptions, ChangeVisibilityOptions | Types |
WebSocket
Section titled “WebSocket”Import from the /ws subpath:
import { connectionStore } from '@venturekit/runtime/ws';connectionStore
Section titled “connectionStore”| Method | Description |
|---|---|
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 |