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>

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
}
src/lib/handler.ts
import { createHandler, setDefaultAuthMiddleware } from '@venturekit/runtime';
setDefaultAuthMiddleware(cookieAuthMiddleware()); // scoped routes
export const apiHandler = createHandler({
middleware: [tenancyMiddleware, memberScopesMiddleware()],
});
// src/routes/....ts — routes shrink to their essence
export 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.

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): void
function _resetDefaultAuthMiddlewareForTesting(): void // test-only: clears the resolution cache

Timing: 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.

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

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)): boolean

Header 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).

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): void
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

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-through
function isRawResult(value: unknown): value is RawResult // detect a wrapped result
export const main = handler(async (_body, ctx) => {
setCookie(ctx, sessionCookie);
return rawResult(redirect('/dashboard'));
});
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>
pathParams?: Record<string, string | undefined>
tx?: unknown
intentOutputs?: Record<string, unknown>
isInternal: boolean
invokeId?: string
cookies: string[]
rawEvent: APIGatewayProxyEventV2
}
  • isInternal — true 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.
  • pathParams — Route [param] segments (e.g. { id: '123' } for GET /users/{id}), overwritten with the parsed value when a params schema is supplied. Read as ctx.pathParams?.['id'].
  • cookies — Outgoing Set-Cookie values; append via setCookie(ctx, …). Drained into the HTTP API v2 response by the runtime.
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
const 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.

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
}
src/routes/health.ts
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.

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';

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.

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.

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.

FunctionDescription
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
ExportDescription
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
IdempotencyConflictErrorA request with the same key is already in progress
MissingIdempotencyKeyErrorThe key was required but absent

createMemorySagaStore() is the in-memory SagaStore counterpart for saga() in tests. See the Idempotency and Saga guides.

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 source
await publishEvent('plan.changed', payload, { busId: 'domain', source: 'admin-api' });
// Batch — chunked into PutEvents calls of 10
await publishEvents([
{ detailType: 'order.placed', detail: order },
{ detailType: 'stock.reserved', detail: reservation, options: { busId: 'inventory' } },
]);
ExportDescription
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
PublishEventOptionsbusId (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 transaction
import { 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.ts
import { 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 }
ExportDescription
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 / OutboxStoreThe 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, RelayResultEnvelope and result types
import { createSecretsClient } from '@venturekit/runtime/secrets';
const secrets = createSecretsClient({ defaultStore: 'secretsmanager', cacheTtlMs: 300_000 });
const apiKey = await secrets.require('stripe-key'); // throws when missing
const maybe = await secrets.get('optional-flag'); // undefined when missing
secrets.invalidate('stripe-key'); // or invalidate() for everything
ExportDescription
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, SecretsClientOptionsTypes

Pairs with the secrets intent, which provisions the entries.

The root rateLimitMiddleware(options) needs a store. Presets and stores live on the /rate-limit subpath:

ExportDescription
RATE_LIMIT_PRESETSrelaxed (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)

What vk generate openapi uses; available programmatically:

ExportDescription
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, HttpMethodTypes
import { createTestHandler, mockEvent, mockContext, mockLogger } from '@venturekit/runtime/testing';
const call = createTestHandler(main); // wraps a Lambda handler
const res = await call({ method: 'POST', body: { title: 'x' }, claims: { sub: 'u1', scope: 'tasks.write' } });
ExportDescription
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>

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:

ExportDescription
createRedisDriver(config)Redis-backed QueueDriver used by vk dev (QueueConfig)
QueueDriversendMessage, sendMessages, receiveMessage, deleteMessage, changeMessageVisibility, purgeQueue, getQueueStats, close
QueueMessage, QueueStats, SendMessageOptions, SendMessageResult, ReceiveMessageOptions, ChangeVisibilityOptionsTypes

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