DBOS Contexts
Background
DBOS automatically supplies a context to each registered function. A function can use its context to call other DBOS functions, interact with the runtime or the database, and access the logger. Each DBOS function has a specific context:
- Contexts used within DBOS functions inherit from
DBOSContext
. - Handlers use
HandlerContext
. - Workflows use
WorkflowContext
. - Transactions use
TransactionContext<T>
with a specific database client type. - Steps use
StepContext
. - Initialization functions use
InitContext
. - Middleware functions use
MiddlewareContext
. - Event receivers use
DBOSExecutorContext
.
DBOSContext
Many contexts inherit from DBOSContext
and share its properties and methods. (InitContext
and MiddlewareContext
are exceptions, as these are applied outside the context of DBOS functions.)
Properties
Methods
ctxt.request
readonly request: HTTPRequest
An object with information about the originating HTTP request that triggered this function (directly or indirectly).
interface HTTPRequest {
readonly headers?: IncomingHttpHeaders; // A node's http.IncomingHttpHeaders object.
readonly rawHeaders?: string[]; // Raw headers.
readonly params?: unknown; // Parsed path parameters from the URL.
readonly body?: unknown; // parsed HTTP body as an object.
readonly rawBody?: string; // Unparsed raw HTTP body string.
readonly query?: ParsedUrlQuery; // Parsed query string.
readonly querystring?: string; // Unparsed raw query string.
readonly url?: string; // Request URL.
readonly ip?: string; // Request remote address.
}
ctxt.workflowUUID
readonly workflowUUID: string
The current workflow's identity UUID, a string uniquely identifying a workflow execution. In a transaction or step, this field is set to the identity UUID of the calling workflow. In a handler, this field is empty.
ctxt.authenticatedUser
readonly authenticatedUser: string
The identity of the authenticated user who ran this function. Authenticated users are set by authentication middleware and inherited through the calling chain.
ctxt.authenticatedRoles
readonly authenticatedRoles: string[];
A list of roles the authenticated user has, if any. Authenticated roles are set by authentication middleware and inherited through the calling chain.
ctxt.assumedRole
readonly assumedRole: string;
The role used to run this function. Empty string if authorization is not required. DBOS's authorization sets the assumed role right before executing a function and this property is not inherited through the calling chain.
ctxt.logger
readonly logger: DBOSLogger
A reference to DBOS's logger. Please see our logging tutorial for more information.
ctxt.span
readonly span: Span
An OpenTelemetry Span associated with this context. You can assign custom trace attributes to this span. Please see the Span API for more information.
ctxt.getConfig
getConfig<T>(key: string): T | undefined;
getConfig<T>(key: string, defaultValue: T): T;
Retrieves an application property specified in the application section of the configuration. Optionally accepts a default value, returned when the key cannot be found in the configuration.
HandlerContext
Handlers use HandlerContext
to invoke other functions, interact with active workflows, and interact directly with HTTP requests and responses.
Properties
Methods
- invoke(target)
- invokeWorkflow(target, [workflowID])
- startWorkflow(target, [workflowID], [queue])
- retrieveWorkflow(workflowID)
- send(destinationUUID, message, [topic, idempotencyKey])
- getEvent(workflowID, key, [timeoutSeconds])
handlerCtxt.koaContext
koaContext: Koa.Context;
The Koa Context of the current request, giving handlers access to the raw HTTP request and response.
handlerCtxt.invoke
invoke<T>(target: T, workflowID?: string): InvokeFuncs<T>
Invoke a transaction or step on the target
class or configured instance.
To invoke workflows, use invokeWorkflow
or startWorkflow
instead.
The syntax for invoking function fn
in class Cls
with argument arg
is:
const output = await handlerCtxt.invoke(Cls).fn(arg);
You don't supply a context to the invoked function—the DBOS Transact runtime does this for you. You can optionally provide an idempotency key to the invoked function. For more information, see our idempotency tutorial.
handlerCtxt.invokeWorkflow
invokeWorkflow<T>(target: T, workflowID?: string): InvokeFuncs<T>
Invoke a workflow and wait for it to complete, returning its result.
To start a workflow without waiting for it to complete, use startWorkflow
.
The syntax for invoking workflow wf
in class Cls
with argument arg
is:
const output = await handlerCtxt.invokeWorkflow(Cls).wf(arg);
You don't supply a context to the invoked workflow—the DBOS Transact runtime does this for you.
handlerCtxt.startWorkflow
startWorkflow<T>(target: T, workflowID?: string, queue?: WorkflowQueue): InvokeFuncs<T>
Start or enqueue a workflow and return a handle to it.
This does not wait for the workflow to complete, though the resulting handle can be used to wait for the workflow result.
To start a workflow and wait for the result, see invokeWorkflow
.
The startWorkflow
method resolves after the handle is durably created; at this point the workflow is guaranteed to run to completion even if the handler is interrupted.
The syntax for starting workflow wf
in class Cls
with argument arg
is:
const workflowHandle = await handlerCtxt.startWorkflow(Cls).wf(arg);
If the workflowID
argument is provided, the workflow will execute exactly once per the specified ID.
If the queue
argument is provided, the workflow may not start immediately. Start of execution will be determined by the queue and its contents.
You don't supply a context to the newly started workflow—the DBOS Transact runtime does this for you.
handlerCtxt.retrieveWorkflow
retrieveWorkflow<R>(workflowID: string): WorkflowHandle<R>
Returns a workflow handle to the workflow with identity workflowID
.
R
is the return type of the target workflow.
handlerCtxt.send
send<T extends NonNullable<any>>(destinationUUID: string, message: T, topic?: string, idempotencyKey?: string): Promise<void>
Sends a message to workflow destinationUUID
.
Messages can optionally be associated with a topic.
You can provide an optional idempotency key to guarantee only a single message is sent even if send
is called more than once.
For more information, see our messages API tutorial.
handlerCtxt.getEvent
getEvent<T extends NonNullable<any>>(workflowID: string, key: string, timeoutSeconds?: number): Promise<T | null>
Retrieves an event published by workflowID
for a given key using the events API.
Awaiting on the promise returned by getEvent()
waits for the workflow to publish the key, returning null
if the wait times out.
handlerCtxt.getWorkflows
getWorkflows(input: GetWorkflowsInput): Promise<GetWorkflowsOutput>
This function allows querying workflow execution history. Its input is an object describing which workflows to retrieve (by default, retrieve all workflows):
export interface GetWorkflowsInput {
workflowName?: string; // The name of the workflow function
authenticatedUser?: string; // The user who ran the workflow.
startTime?: string; // Timestamp in RFC 3339 format
endTime?: string; // Timestamp in RFC 3339 format
status?: "PENDING" | "SUCCESS" | "ERROR" | "RETRIES_EXCEEDED" | "CANCELLED" ; // The status of the workflow.
applicationVersion?: string; // The application version that ran this workflow.
limit?: number; // Return up to this many workflows IDs. IDs are ordered by workflow creation time.
}
It returns as output an object containing a list of the UUIDs of all retrieved workflows, ordered by workflow creation time:
export interface GetWorkflowsOutput {
workflowUUIDs: string[];
}
To obtain further information about a particular workflow, call retrieveWorkflow
on its UUID to obtain its handle.
WorkflowContext
Workflows use WorkflowContext
to invoke other functions and interact with other workflows.
Methods
- invoke(target)
- invokeWorkflow(target)
- startWorkflow(target)
- send(destinationUUID, message, [topic])
- recv([topic, timeoutSeconds])
- setEvent(key, value)
- getEvent()
- retrieveWorkflow(workflowID)
- sleep(durationSec)
- sleepms(durationMS)
workflowCtxt.invoke
invoke<T>(target: T, workflowID?: string): InvokeFuncs<T>
Invoke transactions and steps.
To invoke other workflows, use invokeWorkflow
or startWorkflow
.
The syntax for invoking function fn
in class Cls
with argument arg
is:
const output = await workflowCtxt.invoke(Cls).fn(arg);
You don't supply a context to the invoked function—the DBOS Transact runtime does this for you.
workflowCtxt.invokeWorkflow
invokeWorkflow<T>(target: T)
Invoke a child workflow and wait for it to complete, returning its result.
To start a workflow without waiting it to complete, use startWorkflow
.
The syntax for invoking workflow wf
in class Cls
with argument arg
is:
const output = await ctxt.invokeWorkflow(Cls).wf(arg);
You don't supply a context to the invoked child workflow—the DBOS Transact runtime does this for you.
workflowCtxt.startWorkflow
startWorkflow<T>(target: T, workflowID?: string, queue?: WorkflowQueue).workflowFunction(args)
Start a child workflow and return a handle to it but do not wait for the workflow to complete.
This method resolves after the handle is durably created; at this point the workflow is guaranteed to run to completion.
The syntax for starting workflow wf
in class Cls
with argument arg
is:
const workflowHandle = await ctxt.startWorkflow(Cls).wf(arg);
If the workflowID
argument is provided, the workflow will execute exactly once per the specified ID.
If the queue
argument is provided, the workflow may not start immediately. Start of execution will be determined by the queue and its contents.
You don't supply a context to the newly started child workflow—the DBOS Transact runtime does this for you.
workflowCtxt.invokeChildWorkflow
Deprecated in favor of workflowCtxt.invokeWorkflow
, which is equivalent but syntactically simpler.
workflowCtxt.startChildWorkflow
Deprecated in favor of workflowCtxt.startWorkflow
, which is equivalent but syntactically simpler.
workflowCtxt.send
send<T extends NonNullable<any>>(destinationUUID: string, message: T, topic?: string): Promise<void>
Sends a message to destinationUUID
.
Messages can optionally be associated with a topic.
For more information, see our messages API tutorial.
workflowCtxt.recv
recv<T extends NonNullable<any>>(topic?: string, timeoutSeconds?: number): Promise<T | null>
Receive messages sent to the workflow, optionally for a particular topic.
Messages are dequeued first-in, first-out, from a queue associated with the topic.
Calls to recv()
wait for the next message in the queue, returning null
if the wait times out.
If no topic is specified, recv
can only access messages sent without a topic.
For more information, see our messages API tutorial.
workflowCtxt.setEvent
setEvent<T extends NonNullable<any>>(key: string, value: T): Promise<void>
Creates or updates an event named key
with value value
.
Workflows and HTTP handlers can read events by calling getEvent
with the workflow's UUID.
Events are mutable. Attempting to emit an event twice from a given workflow instance will update the value, but care should be taken to ensure that the value is calculated deterministically for consistency when workflows are recovered.
For more information, see our events API tutorial.
workflowCtxt.getEvent
getEvent<T extends NonNullable<any>>(workflowID: string, key: string, timeoutSeconds?: number): Promise<T | null>
Retrieves an event published by workflowID
for a given key using the events API.
Awaiting on the promise returned by getEvent()
waits for the workflow to set the key, returning null
if the wait times out.
workflowCtxt.retrieveWorkflow
retrieveWorkflow<R>(workflowID: string): WorkflowHandle<R>
Returns a workflow handle to the workflow with identity workflowID.
R
is the return type of the target workflow.
WorkflowContext.sleep
sleep(durationSec: number): Promise<void>
Sleep for durationSec
seconds.
The wakeup time is set in the database when the function is first called, so if the workflow is re-executed, it will not oversleep.
Alternatively, sleepms
is more precise.
WorkflowContext.sleepms
sleepms(durationMS: number): Promise<void>
Sleep for durationMS
milliseconds.
The wakeup time is set in the database when the function is first called, so if the workflow is re-executed, it will not oversleep.
TransactionContext<T>
Transactions use TransactionContext
to interact with the database.
Generic Type Parameter
TransactionContext
is typed generically based on the application database client in use.
The application database client is configurable in a project's configuration file (app_db_client
).
DBOS currently supports the following clients:
import { Knex } from "knex";
static async exampleTransaction(ctxt: TransactionContext<Knex>, ...)
import { EntityManager } from "typeorm";
static async exampleTransaction(ctxt: TransactionContext<EntityManager>, ...)
import { PrismaClient } from "@prisma/client";
static async exampleTransaction(ctxt: TransactionContext<PrismaClient>, ...)
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
static async exampleTransaction(ctxt: TransactionContext<NodePgDatabase>, ...)
Properties
transactionCtxt.client
client: T; // One of [Knex, EntityManager, PrismaClient, NodePgDatabase]
Provides access to the chosen application database client. A transaction function should only interact with the application database using this client.
StoredProcedureContext
Stored procedures use StoredProcedureContext to interact with the database.
While StoredProcedureContext
supports most of the DBOSContext
methods, it does not support
either the span
property or the getConfig<T>
method.
Methods
storedProcCtxt.query
type QueryResult<TRow> = {
rowCount: number;
rows?: TRow[];
}
query<TRow>(sql: string, ...params: unknown[]): Promise<QueryResult<TRow>>;
Execute a query against the database. Returns an object with query result rows (if any) and the number of rows affected by the query.
StepContext
Steps use StepContext
to retrieve configuration information.
Properties
stepCtxt.retriesAllowed
readonly retriesAllowed: boolean;
Whether the step is automatically retried on failure.
Configurable through the @Step
decorator.
stepCtxt.maxAttempts
readonly maxAttempts: number;
Maximum number of retries for the step.
Configurable through the @Step
decorator.
CommunicatorContext
CommunicatorContext
is a historical synonym for StepContext
, as steps are frequently used to communicate with external systems.
InitContext
Class initialization functions and instance initialize()
methods are provided with an InitContext
, which provides access to configuration information, database access, and a logging facility.
Properties and Methods
InitContext.logger
readonly logger: Logger;
logger
is available to record any interesting successes, failures, or diagnostic information that occur during initialization.
InitContext.queryUserDB
queryUserDB<R>(sql: string, ...params: unknown[]): Promise<R[]>;
Accesses the user database directly with SQL. This approach is to be used with caution, as using a string to represent SQL is not fully database independent and careless formation of the string can lead to SQL injection vulnerabilities.
InitContext.getConfig
getConfig<T>(key: string, defaultValue?: T): T | undefined;
getConfig
retrieves configuration information (from .yaml config file / environment). If key
is not present in the configuration, defaultValue
is returned.
MiddlewareContext
MiddlewareContext
is provided to functions that execute against a request before entry into handler, transaction, and workflow functions. These middleware functions are generally executed before, or in the process of, user authentication, request validation, etc. The context is intended to provide read-only database access, logging services, and configuration information.
Properties and Methods
MiddlewareContext.logger
readonly logger: DBOSLogger;
logger
is available to record any interesting successes, failures, or diagnostic information that occur during middleware processing.
MiddlewareContext.span
readonly span: Span;
span
is the tracing span in which the middleware is being executed.
MiddlewareContext.koaContext
readonly koaContext: Koa.Context;
koaContext
is the Koa context, which contains the inbound HTTP request associated with the middleware invocation.
MiddlewareContext.name
readonly name: string;
name
contains the name of the function (handler, transaction, workflow) to be invoked after successful middleware processing.
MiddlewareContext.requiredRole
readonly requiredRole: string[];
requiredRole
contains the list of roles required for the invoked operation. Access to the function will granted if the user has any role on the list. If the list is empty, it means there are no authorization requirements and may indicate that authentication is not required.
MiddlewareContext.getConfig
getConfig<T>(key: string, deflt: T | undefined) : T | undefined
getConfig
retrieves configuration information (from .yaml config file / environment). If key
is not present in the configuration, defaultValue
is returned.
MiddlewareContext.query
query<C extends UserDatabaseClient, R, T extends unknown[]>(qry: (dbclient: C, ...args: T) => Promise<R>, ...args: T): Promise<R>;
The query
function provides read access to the database.
To provide a scoped database connection and to ensure cleanup, the query
API works via a callback function.
The application is to pass in a qry
function that will be executed in a context with access to the database client dbclient
.
The provided dbClient
will be a Knex
or TypeORM EntityManager
or PrismaClient
depending on the application's choice of SQL access library.
This callback function may take arguments, and return a value.
Example, for Knex:
const u = await ctx.query(
// The qry function that takes in a dbClient and a list of arguments (uname in this case)
(dbClient: Knex, uname: string) => {
return dbClient<UserTable>(userTableName).select("username").where({ username: uname })
},
userName // Input value for the uname argument
);
DBOSExecutorContext
The DBOSExecutorContext
is used by event receivers to get their configuration information and invoke workflows, transactions, or communicators in response to received events.
export interface DBOSExecutorContext
{
readonly logger: Logger;
readonly tracer: Tracer;
getRegistrationsFor(eri: DBOSEventReceiver) : DBOSEventReceiverRegistration[];
workflow<T extends unknown[], R>(wf: WorkflowFunction<T, R>, params: WorkflowParams, ...args: T): Promise<WorkflowHandle<R>>;
transaction<T extends unknown[], R>(txnFn: TransactionFunction<T, R>, params: WorkflowParams, ...args: T): Promise<R>;
external<T extends unknown[], R>(stepFn: StepFunction<T, R>, params: WorkflowParams, ...args: T): Promise<R>;
send<T>(destinationUUID: string, message: T, topic?: string, idempotencyKey?: string): Promise<void>;
getEvent<T>(workflowID: string, key: string, timeoutSeconds: number): Promise<T | null>;
retrieveWorkflow<R>(workflowID: string): WorkflowHandle<R>;
getEventDispatchState(svc: string, wfn: string, key: string): Promise<DBOSEventReceiverState | undefined>;
upsertEventDispatchState(state: DBOSEventReceiverState): Promise<DBOSEventReceiverState>;
queryUserDB(sql: string, params?: unknown[]): Promise<unknown[]>;
userDBListen(channels: string[], callback: DBNotificationCallback): Promise<DBNotificationListener>;
}
Properties and Methods
DBOSExecutorContext.logger
readonly logger: Logger
A reference to DBOS's global logger. Event receivers may log information related to event dispatch to this logger. Please see our logging tutorial for more information.
DBOSExecutorContext.tracer
readonly tracer: Tracer;
A reference to DBOS's tracer. Event receivers may initiate or propagate tracing information via tracer
.
Please see our logging tutorial for more information.
DBOSExecutorContext.getConfig
getConfig<T>(key: string, defaultValue: T | undefined) : T | undefined
getConfig
retrieves configuration information (from .yaml config file / environment). If key
is not present in the configuration, defaultValue
is returned.
DBOSExecutorContext.getRegistrationsFor
export interface DBOSEventReceiverRegistration {
methodConfig: unknown,
classConfig: unknown,
methodReg: MethodRegistrationBase
}
getRegistrationsFor(eri: DBOSEventReceiver) : DBOSEventReceiverRegistration[];
getRegistrationsFor
provides a list of all method registrations associated with the specified DBOSEventReceiver
. Each method registration includes configuration and dispatch information:
classConfig
: Any configuration information collected by class-level decoratorsmethodConfig
: Any configuration information collected by method-level decoratorsmethodReg
: Reference to the method to be called for each event
DBOSExecutorContext.workflow
workflow<T extends unknown[], R>(
wf: WorkflowFunction<T, R>, params: WorkflowParams, ...args: T
) : Promise<WorkflowHandle<R>>;
Invokes the provided wf
workflow function, with inputs specified by args
. The WorkflowParams
control how the workflow is started:
WorkflowParams.workflowUUID
: Set the workflow idempotency key, for OAOO.WorkflowParams.queueName
: Indicate that the workflow is to be run in a queue, with the provided name. The queue with the providedqueueName
must have been created and registered prior to executingworkflow
, as the queue provides necessary concurrency and rate-limiting information.
The return value of workflow
is a WorkflowHandle
for the running or queued workflow.
DBOSExecutorContext.transaction
transaction<T extends unknown[], R>(
txnFn: TransactionFunction<T, R>, params: WorkflowParams, ...args: T
) : Promise<R>;
Invokes a single-operation workflow consisting of the provided txnFn
function, with inputs specified by args
. For additional information, see DBOSExecutorContext.workflow
.
DBOSExecutorContext.external
external<T extends unknown[], R>(
stepFn: StepFunction<T, R>, params: WorkflowParams, ...args: T
) : Promise<R>;
Invokes a single-operation workflow consisting of the provided stepFn
function, with inputs specified by args
. For additional information on WorkflowParams
, see DBOSExecutorContext.workflow
.
DBOSExecutorContext.send
send<T extends NonNullable<any>>(destinationID: string, message: T, topic?: string, idempotencyKey?: string): Promise<void>
Sends a message to the workflow identified by destinationID
.
Messages can optionally be associated with a topic.
You can provide an optional idempotency key to guarantee only a single message is sent even if send
is called more than once.
For more information, see our messages API tutorial.
DBOSExecutorContext.getEvent
getEvent<T extends NonNullable<any>>(workflowID: string, key: string, timeoutSeconds?: number): Promise<T | null>
Retrieves an event published by workflowID
for a given key using the events API.
Awaiting on the promise returned by getEvent()
waits for the workflow to set the key, returning null
if the wait times out.
DBOSExecutorContext.retrieveWorkflow
retrieveWorkflow<R>(workflowID: string): WorkflowHandle<R>
Returns a workflow handle to the workflow with identity workflowID
.
R
is the return type of the target workflow.
DBOSExecutorContext.upsertEventDispatchState
upsertEventDispatchState(state: DBOSEventReceiverState): Promise<DBOSEventReceiverState>;
export interface DBOSEventReceiverState
{
service: string;
workflowFnName: string;
key: string;
value?: string;
updateTime?: number;
updateSeq?: bigint;
}
An event receiver may keep state in the system database. This state may be helpful for backfilling events that came in while the event receiver was not running. This state uses a key/value store design, where the event receiver may use upsertEventDispatchState
to insert/update the value associated with a key, and retrieve the value associated with a key. This implementation also supports the notion of an update time or update sequence; updates made with lower sequence numbers or times are discared if the existing entry is marked with a later sequence / time.
The key consists of:
service
:service
should be unique to the event receiver keeping state, to separate from other table usersworkflowFnName
:workflowFnName
workflow function name should be the fully qualified / unique function name dispatched, to keep state separate by event functionkey
: Thekey
field allows multiple records per service / workflow function
The value stored for each service
/workflowFnName
/key
combination includes:
value
:value
is a string value. JSON can be used to encode more complex values.updateTime
: The timevalue
was set. Upserts of records with an earlierupdateTime
will have no effect on the stored state.updateSeq
: An integer number indicating when the value was set. Upserts of records with a smallerupdateSeq
will have no effect on the stored state.
upsertEventDispatchState
inserts a value associated with a key. If a value is already associated with the specified key, the stored value will be updated, unless updateTime
or updateSeq
is provided, and is less that what is already stored in the system database.
The function return value indicates the contents of the system database for the specified key. This is useful to detect if a more recent record is alreadys stored in the database.
DBOSExecutorContext.getEventDispatchState
getEventDispatchState(service: string, workflowFnName: string, key: string)
: Promise<DBOSEventReceiverState | undefined>;
Retrieve the value set for an event receiver's key, as stored by upsertEventDispatchState
above. If no value has been associated with the combination of service
/workflowFnName
/key
above, then undefined
is returned.
DBOSExecutorContext.queryUserDB
queryUserDB(sql: string, params?: unknown[]): Promise<unknown[]>;
Executes the provided sql
template against the default user application database, using params
.
DBOSExecutorContext.userDBListen
interface DBNotification {
channel: string;
payload?: string;
}
type DBNotificationCallback = (n: DBNotification) => void;
interface DBNotificationListener {
close(): Promise<void>;
}
userDBListen(channels: string[], callback: DBNotificationCallback): Promise<DBNotificationListener>;
userDBListen
listens for notifications within the default user application database:
channels
is a list of notification channels of interestcallback
will be executed for each notification received The return value ofuserDBListen
is aDBNotificationListener
which should be used toclose
the listener and stop the listening operation cleanly.
callback
is the function that will be called when notifications arrive; it is provided with a DBNotification
containing the channel
and optional payload
of the received notification.
Information Available Outside Of Contexts
While most code is executed within one of the numerous DBOS contexts, there are a few exceptions, such as the HTTP server, its non-DBOS middleware, or background tasks. For these cases, it is possible to access the globalLogger
and dbosConfig
from a global location:
import { DBOS } from "@dbos-inc/dbos-sdk";
function myFunc() {
DBOS.globalLogger?.info(`There is no context here, but I need to log something anyway!
My config is '${dbosConfig?.application?.myvalue}'`);
}
The definition of DBOS
is:
class DBOS {
static globalLogger?: GlobalLogger; // The global logger
static dbosConfig?: DBOSConfig; // The global DBOS configuration
}
Note that DBOS
is not fully available util runtime initialization starts.