Coming from the Temporal SDK
If you already write Temporal workflows in TypeScript, you know most of Norn. It runs on the official SDK (1.24), so the execution model, the workflow sandbox, determinism, history, replay, task queues, retries and versioning are all unchanged. Norn changes how you declare things: classes and decorators instead of defineSignal() and string names, and a client typed from the workflow class.
What stays the same
Section titled “What stays the same”- Inside workflows you still import
sleep,condition,patched,workflowInfo,CancellationScopeand the rest from@temporalio/workflow. - Inside activities you still import
heartbeat,ContextandApplicationFailurefrom@temporalio/activityand@temporalio/common. - The workflow sandbox and its rules.
- Data converters, interceptors, plugins, schedules, worker versioning and the Web UI.
- Every Norn wrapper exposes the SDK object underneath:
norn.temporalandhandle.temporalare the SDK client and handle, andcreateWorker()passesworkerOptionsandpluginsthrough toWorker.create(). If Norn doesn’t cover something, use the SDK.
The mapping
Section titled “The mapping”| Temporal SDK | Norn | Guide |
|---|---|---|
export async function orderWorkflow(input) |
@Workflow({ taskQueue, id }) on a class; run(input) is the body; the type name is the class name |
Workflows |
defineSignal() + setHandler() |
@Signal() on a method |
Workflows |
defineQuery() + setHandler() |
@Query() on a method |
Workflows |
defineUpdate() + setHandler(..., { validator }) |
@Update() and @Validate('method') |
Workflows |
proxyActivities<typeof acts>({ startToCloseTimeout, retry }) |
useActivities<PaymentActivities>('payments', { timeout, retries }) |
Activities |
retry.nonRetryableErrorTypes |
nonRetryable: ['CardDeclinedError'] |
Activities |
An activities object of functions |
A plain class, registered as activities: { payments: new PaymentActivities(gateway) } |
Activities |
Worker.create({ workflowsPath, activities, taskQueue }) |
createWorker({ workflows, activities }): one worker per task queue your workflows declare, with graceful shutdown |
Worker |
client.workflow.start('orderWorkflow', { taskQueue, workflowId, args }) |
norn.workflow(OrderWorkflow).start(input): name, task queue and ID come from the class |
Client |
client.workflow.getHandle(\order-${id}`)` |
norn.workflow(OrderWorkflow).get({ orderId }) |
Client |
executeChild() / startChild() |
useChild(ChildWorkflow) |
Workflows |
getExternalWorkflowHandle() |
useExternal(OtherWorkflow, { id fields }) |
Workflows |
continueAsNew<typeof wf>(...) |
continueAsNew(...), typed against run() |
Workflows |
upsertSearchAttributes() with defineSearchAttributeKey() |
searchAttributes in @Workflow, then setSearchAttributes() |
Workflows |
Catching ActivityFailure and checking cause.type |
asFailure(error, 'CardDeclinedError') |
Workflows |
WorkflowExecutionAlreadyStartedError, WorkflowNotFoundError, … on the client |
NornError subclasses with a code and an HTTP status |
Client |
TestWorkflowEnvironment + a hand-written activities object |
useTestEnv({ workflows, activities }) with fake activity objects |
Testing |
bundleWorkflowCode() |
bundleWorkflows(workflows, { outFile }) |
Worker |
What Norn adds
Section titled “What Norn adds”- A client typed from the workflow. Pass the class to
norn.workflow(). Its input, result, and the arguments and results of its signals, queries and updates are all checked at compile time. - ID templates.
id: 'order-{orderId}'is checked againstrun()’s input and used everywhere the workflow is started or looked up. - Errors with codes. Client failures become
NornErrors with a stablecodeand an HTTP status, so an API can map them without string matching.
Things to know
Section titled “Things to know”- Decorators don’t change types, so TypeScript can’t tell a signal method from a query method.
handle.signal('getStatus')compiles, and fails when it runs. Argument and result types are always checked. - Activities are registered as
<namespace>.<method>, such aspayments.charge. Keep the namespace and method names stable while workflows that call them are running, and keep workflow and handler names stable too: running executions depend on them. - To move an existing SDK workflow to Norn, keep its workflow type name (the class name, or
@Workflow({ name })) and every handler name the same, and in-flight executions keep working. Test with the SDK’sWorker.runReplayHistories()before switching over.
Ready to try it? Start with the quickstart.