HighX
Server

The ctx object

Everything a handler is given, and which kind of function gets what.

Every handler's first argument is ctx. What it holds depends on the kind of function, and the difference is enforced by the types rather than by convention.

QueryMutationActionWhat it is
ctx.db●●●The tenant's database
ctx.identity●●●The verified caller
ctx.requestId●●●The correlation ID for this request
ctx.now()—●●The clock. Deliberately absent from queries
ctx.fetch——●HTTP, with the request deadline attached
ctx.global——●The D1 read model
ctx.run——●Run a mutation as its own transaction
ctx.saga——●A durable, replayable sequence of steps
ctx.signal——●Aborts when the deadline passes

ctx.identity

The only trustworthy source of who is calling:

interface Identity {
  readonly provider: string;   // who vouched for this person
  readonly subject: string;    // their stable ID with that provider
  readonly userId: UserId;
  readonly tenantId: TenantId;
  readonly roles: readonly string[];
}

An accountId in args is a client-supplied string and means nothing. Read identity off ctx, always.

ctx.db

ctx.db.query("notes").withIndex("byColor", { color }).take(20);   // indexed read
ctx.db.table("notes").get(id);
ctx.db.table("notes").insert(row);        // mutations only
ctx.db.table("notes").update(id, changes);
ctx.db.table("notes").delete(id);
ctx.db.table("notes").list();             // unindexed scan

Both forms record what they read, which is how a subscription knows what wakes it. → Reactivity

ctx.now()

A query has no clock. That is not an oversight: a query must return the same result for the same data, or the engine could not re-run it on your behalf and diff the outcome. Write the timestamp in the mutation and read the field in the query.

ctx.requestId

Pass it to your logger. It correlates a mutation, the patches it produced, and anything the client reported, across one request.

On this page