Lambda-to-Lambda Invocation
VentureKit uses a single invoke() function for all Lambda-to-Lambda calls:
invoke('route/path', opts)— Call a route handler (builds API Gateway V2 event, skips API Gateway)invoke({ function: 'name' }, opts)— Call a standalone function with a raw payload
Quick Start
Section titled “Quick Start”import { invoke } from '@venturekit/runtime';
// Call a route handler (API GW event format)const result = await invoke<{ id: string; name: string }>('users/{id}', { method: 'GET', pathParams: { id: '42' },});console.log(result.data); // { id: '42', name: 'Alice' }
// Call a standalone function (src/functions/process-orders.ts)const job = await invoke<{ jobId: string }>( { function: 'process-orders' }, { payload: { orderId: 'abc', items: ['widget'] } },);console.log(job.data.jobId);How It Works
Section titled “How It Works”Route invocation (invoke('path', ...))
Section titled “Route invocation (invoke('path', ...))”- Resolves the Lambda function name from the route path (
myapp-users-{id}) - Builds a full API Gateway V2 event payload (same as external HTTP requests)
- Calls Lambda directly via AWS SDK
InvokeCommand— no API Gateway hop - Propagates trace context, tenant ID, user ID, and an
x-venturekit-source: internalheader - Parses the response back into a typed result
Your handler receives an identical event whether called externally (via API Gateway) or internally (via invoke()). No code changes needed.
Function invocation (invoke({ function: 'name' }, ...))
Section titled “Function invocation (invoke({ function: 'name' }, ...))”- Resolves the Lambda name:
{project}-{stage}-fn-{name}(e.g.myapp-dev-fn-process-orders) - Sends the raw payload (JSON-serialized) — no API Gateway event wrapping
- Returns the function’s response as typed data
You only use the short name as it appears in your project (e.g. 'process-orders'). VentureKit adds the project prefix and stage automatically.
Route Handler Invocation
Section titled “Route Handler Invocation”import { invoke } from '@venturekit/runtime';
// GET with query paramsconst users = await invoke<User[]>('users/list', { method: 'GET', queryParams: { page: '1', limit: '20' },});
// POST with bodyconst created = await invoke<User>('users/create', { body: { name: 'Alice', email: 'alice@example.com' },});
// With path parametersconst user = await invoke<User>('users/{id}', { method: 'GET', pathParams: { id: '42' },});
// Fire-and-forget (async invocation)await invoke('notifications/send', { mode: 'async', body: { userId: '42', message: 'Hello!' },});
// Propagate contextawait invoke('orders/create', { body: orderData, trace: ctx.trace, tenantId: ctx.tenant?.id, userId: ctx.user?.sub,});Route Name Resolution
Section titled “Route Name Resolution”route: 'users/list' → Lambda: '{project}-users-list'route: 'tasks/{id}' → Lambda: '{project}-tasks-{id}'Function Invocation
Section titled “Function Invocation”Call standalone functions in src/functions/ by their short name:
import { invoke } from '@venturekit/runtime';
// Sync: call and wait for resultconst result = await invoke<{ jobId: string }>( { function: 'process-orders' }, { payload: { orderId: 'abc-123', items: [...] } },);console.log(result.data.jobId);
// Async: fire-and-forgetawait invoke( { function: 'send-email' }, { payload: { to: 'user@example.com', template: 'welcome' }, mode: 'async' },);
// With trace propagationawait invoke( { function: 'audit-log' }, { payload: { action: 'order.created', data: order }, trace: ctx.trace },);Function Name Resolution
Section titled “Function Name Resolution”function: 'process-orders' → Lambda: '{project}-{stage}-fn-process-orders'function: 'order-create-order' → Lambda: '{project}-{stage}-fn-order-create-order'The project and stage are read from VENTURE_PROJECT_NAME and VENTURE_STAGE environment variables (set automatically during deployment and local dev).
Detecting Internal Calls
Section titled “Detecting Internal Calls”Route invocations include x-venturekit-source: internal and a unique x-venturekit-invoke-id header:
import { handler } from '@venturekit/runtime';
export const main = handler({ fn: async (body, ctx) => { const isInternal = ctx.rawEvent.headers?.['x-venturekit-source'] === 'internal';
if (isInternal) { // Called from another Lambda via invoke() — skip rate limiting, etc. }
return { data: { source: isInternal ? 'internal' : 'external' } }; },});Advanced: Custom Invoker
Section titled “Advanced: Custom Invoker”For full control, use the lower-level createInvoker() API:
import { createInvoker, createLambdaInvoker, defaultFunctionNameResolver } from '@venturekit/runtime';
const lambdaInvoker = await createLambdaInvoker({ region: 'eu-west-1' });const invoker = createInvoker({ invoker: lambdaInvoker, resolver: defaultFunctionNameResolver('my-custom-prefix'),});
const result = await invoker('users/get');IAM Permissions
Section titled “IAM Permissions”Lambda functions need lambda:InvokeFunction permission to call other functions. VentureKit’s CDK stack grants this automatically for functions in the same stack.
Related
Section titled “Related”- Project Structure — organizing routes, functions, and shared logic
- Handlers — writing route handlers
- Saga Pattern — distributed transactions with compensation
- Idempotency — prevent duplicate processing