Skip to content

Pre-GA Design Partner and Early Access only. Request access

Helix Docs

The ctx object

Field-by-field reference for the ctx object Helix functions receive, plus getContext() for shared code and availability by function type.

For developers
View as Markdown

Every Helix function execution receives a ctx object as its handler argument. In the shipped product it carries the project-scoped key-value store, outbound HTTP including authenticated calls, logging, the validated request input, response helpers, execution metadata, and caller identity; db(), queue(), files, ai(), and config are platform design and cannot be called today. Shared code outside the handler can reach the same object with getContext().

Surface

FieldTypeStatusReference
configRecord<string, string>DesignConfiguration
logContextLoggerShippedctx.log
httpKyInstanceShippedctx.http
auth(alias)Promise<Record<string, string>>Shippedctx.auth()
db(alias?)DrizzleInstanceDesignLimits and roadmap
kv()KeyValueStoreShippedctx.kv()
filesFileSystemClientDesignLimits and roadmap
queue(name)QueueClientDesignLimits and roadmap
ai(model?)AIClientDesignLimits and roadmap
inputValidated merged inputShippedRequest and response
method, headers, pathstring, Headers, stringShippedRequest and response
requestRaw request accessorShippedRequest and response
status(code)voidShippedRequest and response
error(status, message)HttpError (throw it)ShippedRequest and response
triggerTrigger data by function typeDesignRequest and response
identityIdentityContext | nullShippedctx.identity
stateRecord<string, any>ShippedRequest and response
executionIdstringShippedThis page, below
userIdstring | nullShippedThis page, below
projectIdstring | nullShippedThis page, below
workspaceIdstring | nullShippedThis page, below
organizationIdstring | nullShippedThis page, below

kv() takes no argument in the shipped product and is always project-scoped; the scope keywords and external aliases on ctx.kv() are design. identity ships, but only user.id populates reliably right now, so treat email, name, groups, roles, and org as arriving with the full Helix Identity rollout. The four fields marked Design are not on the context object today: reaching for ctx.db(), ctx.files, ctx.queue(), or ctx.ai() in a function will not work, and this site does not document them until they ship.

Execution metadata

Five fields identify where an execution runs and who invoked it. Each is null when unknown, for example in an unprovisioned local project.

FieldMeaning
executionIdUUID per invocation, used for correlation and tracing
userIdThe invoking user, mirrors identity.user.id
projectIdThe project this deployment belongs to
workspaceIdThe workspace this deployment belongs to
organizationIdThe organization this deployment belongs to

The SDK resolves these from different sources per environment, so function code never cares:

FieldDeployed (Lambda)Local (helix dev)
executionIdInvocation payload rootx-tray-execution-id header, else generated per request
userIdInvocation payload root, HelixExecutionPrincipal JWT claim as fallbackx-tray-user-id header (null if unset)
projectIdInvocation payload rootprojectId in helix.config.ts (null if unprovisioned)
workspaceIdInvocation payload rootworkspaceId in helix.config.ts (null if unset)
organizationIdInvocation payload rootctx.identity.org.id (default dev identity: dev-org; null when identity is null)

In production the claims come from the execution JWT minted by the platform. The JWT is verified upstream; the SDK decodes it without re-verifying. projectId, workspaceId, and organizationId identify the deployment; userId identifies the caller. The JWT carries exactly these five claims and nothing else about the user, which is why ctx.identity populates incrementally. See ctx.identity.

getContext()

Handlers receive ctx as a parameter. Shared utilities deeper in the call chain can access the same context without threading it through every call:

import { getContext } from '@trayai/helix-sdk';

function getContext(): FunctionContext

The context is stored in Node.js AsyncLocalStorage, scoped per execution, so concurrent requests never bleed into each other. Calling getContext() outside a function execution throws: “getContext() called outside of a Helix function execution.”

// functions/_shared/salesforce.ts
import { getContext } from '@trayai/helix-sdk';

const AUTH = 'salesforce_prod';
const API = '{{instance_url}}/services/data/v59.0';

export async function query<T = any>(soql: string): Promise<T[]> {
  const { http } = getContext(); // no ctx parameter needed
  const result = await http
    .authed(AUTH)
    .get(`${API}/query`, { searchParams: { q: soql } })
    .json<{ records: T[] }>();
  return result.records;
}
SituationUse
Function handler (top level)The ctx parameter
Shared utilities in _shared/getContext()
Unit testing shared coderunWithContext() from the testing module
Code outside a function executionNeither works; the error message says so

runWithContext() for tests

To unit test shared code that calls getContext(), wrap the call in a mock context:

// tests/shared/salesforce.test.ts
import { runWithContext } from '@trayai/helix-sdk/testing';
import { query } from '../functions/_shared/salesforce';

test('query returns contacts', async () => {
  const result = await runWithContext(
    { http: mockKyInstance, kv: mockKvStore },
    () => query('SELECT Id FROM Contact'),
  );

  expect(result).toEqual([{ Id: '003xx...' }]);
});

Availability by function type

Not every field exists on every function type. Accessing an unavailable field throws a clear runtime error naming the function types that support it. defineFunction is the only function type in the shipped product, so the last three columns describe platform design.

Context fielddefineFunctiondefineScheduledefineAppTriggerdefineQueueConsumer
config
log
http
auth()
db()
kv()
files
queue()
input
method
headers
request
status()
error()
trigger.data
trigger.scheduledAt
message
identity✓ (when enabled)
state

For how each function type is declared and routed, see the functions and routing guide.

Loading search…

Jump to a section

tab to move · esc to close