* Send the Python task's execution_timeout to the supervisor * Load the stub task only inside the is_stub branch of ti_run Only a task a language SDK runs needs its definition from the serialized Dag, so keeping every use of it under that branch makes it clear a Python task never loads one. * Let a stub task ask for no execution_timeout A language SDK runtime has to tell a task that sets no timeout, which gets the worker's default, from one that asks for none. Sending zero for the latter needs a negative timeout to stay negative, so the supervisor, not the server, turns it into the grace period alone. * Drop the note on where the default execution_timeout is read * Set the run context's execution_timeout only in the coordinator * Address review nits on execution_timeout docs and schema description * Reword execution_timeout comment for runtimes that send SetExecutionTimeout * Address review nits on ti_run stub task lookup
Airflow TypeScript SDK
Write Apache Airflow Dags in TypeScript, and run them on Node.js.
Status: 1.0.0-beta2 · API may change · Node 22+ · ESM-only
Installation
npm install @apache-airflow/ts-sdk@1.0.0-beta2
npm install --save-dev esbuild
Quick start
Declare a Dag in src/main.ts:
import { Bundle, Dag } from "@apache-airflow/ts-sdk";
const dag = new Dag("ts_hello", { schedule: "@daily", queue: "typescript" });
const extract = dag.task("extract", async (): Promise<number> => 42);
const report = dag.task("report", async ({ rows }: { rows: number }) => {
console.log(`extracted ${rows} rows`);
});
report({ rows: extract() });
await new Bundle(dag).serve();
Build it into a single file:
npx airflow-ts-pack src/main.ts --outdir dist
Put dist/bundle.min.mjs in a Dag bundle, such as the default dags-folder bundle, and send the typescript
queue to the Node.js coordinator in airflow.cfg:
[sdk]
coordinators = {"ts": {"classpath": "airflow.sdk.coordinators.node.NodeCoordinator"}}
queue_to_coordinator = {"typescript": "ts"}
Airflow reads the bundle like any other Dag file: ts_hello shows up in the UI, runs on its schedule, and
report receives the value extract returned.
Learn more
The TypeScript SDK guide covers:
| Topic | Guide section |
|---|---|
| Task inputs, options, order-only dependencies, task groups, branching, triggering another Dag | Declaring a Dag in TypeScript |
approval and hitl tasks that wait for a person |
Human-in-the-loop tasks |
getContext(), getClient() and the TaskClient |
Writing tasks |
Building a bundle with airflow-ts-pack |
Building a bundle |
Where to deploy a bundle, and the NodeCoordinator options |
Deploying |
| What a Dag declared in TypeScript cannot do yet | Limitations |
See example/ for a working project.
Compatibility matrix
Which Airflow TaskInstance states and capabilities this SDK supports. This table is generated from
capabilities.yaml;
the conformance dimensions are defined in the
Language SDK conformance spec.
Do not edit the table by hand. Update the manifest and run the update-ts-sdk-readme-matrix prek hook.
Min. Airflow version: 3.5.0 · supervisor schema: 2027-02-28
| Dimension | Tier | Supported | Since (Airflow) | Since (SDK) | Notes |
|---|---|---|---|---|---|
| TaskInstance states | |||||
state: success |
MUST | ✓ | 3.4.0 | 1.0.0 | |
state: failed |
MUST | ✓ | 3.4.0 | 1.0.0 | |
state: up_for_retry |
MUST | ✓ | 3.4.0 | 1.0.0 | RetryTask |
state: skipped |
SHOULD | ✓ | 3.4.0 | 1.0.0 | triggerDagRun with skipWhenAlreadyExists only |
state: deferred |
MAY | ✓ | 3.4.0 | 1.0.0 | triggerDagRun with deferrable only |
state: up_for_reschedule |
MAY | ✗ | – | – | runtime does not emit RescheduleTask yet |
state: awaiting_input |
MAY | ✓ | 3.4.0 | 1.0.0 | approval() / hitl() in a Dag definition |
state: removed |
MAY | ✓ | 3.4.0 | 1.0.0 | |
| Task runtime | |||||
capability: task-logging |
MUST | ✓ | 3.4.0 | 1.0.0 | structured records over the log socket |
capability: xcom-read-write |
MUST | ✓ | 3.4.0 | 1.0.0 | getXCom / setXCom |
capability: connection-read |
MUST | ✓ | 3.4.0 | 1.0.0 | getConnection |
capability: variable-read-write |
MUST | ✓ | 3.4.0 | 1.0.0 | getVariable / setVariable / deleteVariable |
capability: self-contained-bundle |
MUST | ✓ | 3.4.0 | 1.0.0 | Airflow metadata embedded in the bundle |
capability: retry-policy |
MAY | ✗ | – | – | no task-facing retry-policy API yet |
capability: task-state-store |
MAY | ✓ | 3.4.0 | 1.0.0 | taskStateStore.get / set / delete / clear |
capability: asset-state-store |
MAY | ✗ | – | – | no task-facing asset state-store API yet |
capability: asset-event-emit |
MAY | ✗ | – | – | runtime does not emit asset events yet |
capability: asset-event-read |
MAY | ✗ | – | – | no task-facing asset-event API yet |
capability: object-store |
MAY | ✗ | – | – | no object-storage API yet |
capability: listeners |
MAY | ✗ | – | – | no hook into the Python listener API, so a task emits no OpenLineage facets of its own |
| Dag definition | |||||
capability: dag-definition-authoring |
SHOULD | ✓ | 3.4.0 | 1.0.0 | new Dag |
capability: dag-args |
MUST † | ✓ | 3.4.0 | 1.0.0 | |
capability: task-args |
MUST † | ✓ | 3.4.0 | 1.0.0 | |
capability: dag-params |
MUST † | ✗ | – | – | |
capability: dag-callbacks |
MAY † | ✗ | – | – | |
capability: explicit-dependencies |
MUST † | ✓ | 3.4.0 | 1.0.0 | before / after |
capability: edge-labels |
MAY † | ✓ | 3.4.0 | 1.0.0 | label(task, text) |
capability: taskflow-dependencies |
MUST † | ✓ | 3.4.0 | 1.0.0 | |
capability: task-group |
MAY † | ✓ | 3.4.0 | 1.0.0 | dag.taskGroup |
capability: task-loops |
MAY † | ✗ | – | – | |
capability: if-else-branching |
SHOULD † | ✓ | 3.4.0 | 1.0.0 | dag.if |
capability: switch-case-branching |
SHOULD † | ✓ | 3.4.0 | 1.0.0 | dag.switch |
capability: trigger-dag-run |
MAY † | ✓ | 3.4.0 | 1.0.0 | triggerDagRun |
capability: dynamic-dag-generation |
MAY † | ✓ | 3.4.0 | 1.0.0 | |
capability: human-in-the-loop |
MAY † | ✓ | 3.4.0 | 1.0.0 | approval() / hitl() |
capability: dag-test |
SHOULD † | ✗ | – | – | |
capability: asset-definition |
MAY † | ✗ | – | – | |
capability: asset-scheduling |
MAY † | ✗ | – | – | |
capability: mapped-tasks |
MAY † | ✗ | – | – | |
capability: asset-inlets-outlets |
MAY † | ✗ | – | – | |
capability: task-callbacks |
MAY † | ✗ | – | – | no SDK-defined task callbacks |
| Python Dag with stub TaskHandler | |||||
capability: mixed-lang-stub-target |
MUST | ✓ | 3.4.0 | 1.0.0 | @task.stub |
capability: taskflow-binding |
MUST | ✓ | 3.4.0 | 1.0.0 | mapped stubs excluded |
capability: mapped-tasks |
MAY | ✗ | – | – | Airflow does not bind arguments to mapped stubs yet |
capability: asset-inlets-outlets |
MAY | ✗ | – | – | runtime does not report task outlets yet |
capability: task-callbacks |
MAY | ✗ | – | – | a Python-declared task callback does not run for a stub task |
Marks: ✓ supported · ✗ not supported · n/a not applicable. A tier marked † applies only when dag-definition-authoring is supported.
Links
- TypeScript SDK guide (staged docs) (how Airflow runs TypeScript task handlers)
- API reference (staged) (generated from the TypeScript sources)
- Source: the
ts-sdk/directory of the Apache Airflow monorepo - Issues: bug reports and feature requests
- Website · Slack
- Developing this package (local build, docs, and the release workflow)