This is an automated email from the ASF dual-hosted git repository.
jason810496 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new e0f837b1cb1 Add TypeScript SDK guide to language SDKs docs (#70219)
e0f837b1cb1 is described below
commit e0f837b1cb188429810045bbe51f93989066b764
Author: Jason(Zhe-You) Liu <[email protected]>
AuthorDate: Fri Jul 24 11:50:41 2026 +0800
Add TypeScript SDK guide to language SDKs docs (#70219)
---
.../language-sdks/index.rst | 5 +
.../language-sdks/typescript.rst | 294 +++++++++++++++++++++
docs/spelling_wordlist.txt | 2 +
task-sdk/docs/api.rst | 4 +
ts-sdk/README.md | 8 +-
5 files changed, 308 insertions(+), 5 deletions(-)
diff --git a/airflow-core/docs/authoring-and-scheduling/language-sdks/index.rst
b/airflow-core/docs/authoring-and-scheduling/language-sdks/index.rst
index 7272444cba3..79a0cb3df1f 100644
--- a/airflow-core/docs/authoring-and-scheduling/language-sdks/index.rst
+++ b/airflow-core/docs/authoring-and-scheduling/language-sdks/index.rst
@@ -44,12 +44,17 @@ in a non-Python implementation.
-
:class:`task-sdk:airflow.sdk.coordinators.executable.ExecutableCoordinator`
- None (native binary)
- :doc:`go`
+ * - TypeScript
+ - :class:`task-sdk:airflow.sdk.coordinators.node.NodeCoordinator`
+ - Node.js 22
+ - :doc:`typescript`
.. toctree::
:hidden:
java
go
+ typescript
How it works
------------
diff --git
a/airflow-core/docs/authoring-and-scheduling/language-sdks/typescript.rst
b/airflow-core/docs/authoring-and-scheduling/language-sdks/typescript.rst
new file mode 100644
index 00000000000..87e12cea474
--- /dev/null
+++ b/airflow-core/docs/authoring-and-scheduling/language-sdks/typescript.rst
@@ -0,0 +1,294 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. _typescript-sdk:
+
+TypeScript SDK
+==============
+
+|experimental|
+
+The TypeScript SDK lets you implement Airflow task logic in TypeScript (or
plain JavaScript), running on
+Node.js. The Dag and its scheduling remain in Python; individual tasks
delegate to a Node.js subprocess that
+is spawned by :class:`~airflow.sdk.coordinators.node.NodeCoordinator` for each
task instance.
+
+The SDK is the ``@apache-airflow/ts-sdk`` package (ESM-only). It is currently
in **alpha** and its API may change.
+
+.. warning::
+
+ The SDK is not yet published to npm. To try it today, build it from source
in the
+ `ts-sdk/ <https://github.com/apache/airflow/tree/main/ts-sdk>`__ directory
of the Airflow repository and
+ depend on it locally (see ``ts-sdk/example/`` for a working setup).
+
+.. contents:: Contents
+ :local:
+ :depth: 2
+
+Prerequisites
+-------------
+
+* Node.js 22 or later must be available on the Airflow worker nodes.
+* The packed bundle (a single ``bundle.mjs`` file, see
:ref:`typescript-sdk/build`) must be accessible from
+ the worker, under a directory the coordinator scans.
+* The ``apache-airflow-task-sdk`` package (installed with Airflow) provides
the coordinator; no additional
+ Python packages are needed.
+
+Quick start
+-----------
+
+The following example shows the minimal moving parts: a Python Dag with a stub
task, and a TypeScript
+implementation of that task.
+
+Python Dag (the scheduling side)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: python
+
+ from airflow.sdk import dag, task
+
+
+ @dag
+ def typescript_example():
+ @task
+ def python_start():
+ return "hello from Python"
+
+ @task.stub(queue="typescript")
+ def build_message(): ...
+
+ python_start() >> build_message()
+
+
+ typescript_example()
+
+``@task.stub`` declares the *shape* of the TypeScript task without any Python
implementation. The ``queue``
+value routes the task to the Node.js coordinator.
+
+TypeScript implementation
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A task is an ordinary (usually ``async``) function receiving
``TaskHandlerArgs``. Register it with the
+``dag_id`` and ``task_id`` it implements, then start the coordinator runtime;
the registrations and the
+top-level ``await startCoordinator()`` make the module a runnable bundle entry
point.
+
+.. code-block:: typescript
+
+ import { registerTask, startCoordinator, type TaskHandlerArgs } from
"@apache-airflow/ts-sdk";
+
+ export async function buildMessage({ ctx, client }: TaskHandlerArgs) {
+ const upstream = await client.getXCom<string>({
+ key: "return_value",
+ taskId: "python_start",
+ });
+ const greeting = await client.getVariable("typescript_example_greeting");
+ return `${greeting ?? "hello from TypeScript"}; upstream=${upstream ??
"missing"}`;
+ }
+
+ registerTask({ dagId: "typescript_example", taskId: "build_message" },
buildMessage);
+
+ await startCoordinator();
+
+The ``dagId`` passed to ``registerTask`` must match the ``dag_id`` of the
Python Dag, and each ``taskId``
+must match a ``@task.stub`` function in that Dag.
+
+.. note::
+
+ As with the other language SDKs, XCom *dependencies* are declared in the
Python stub Dag (they define task
+ order). The value must still be read explicitly in TypeScript via
``client.getXCom``, and produced either
+ by the task's return value or by ``client.setXCom``.
+
+Coordinator configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Register the coordinator and route the queue to it under ``[sdk]`` in
``airflow.cfg`` (or the equivalent
+``AIRFLOW__SDK__*`` environment variables):
+
+.. code-block:: ini
+
+ [sdk]
+ coordinators = {
+ "ts": {
+ "classpath": "airflow.sdk.coordinators.node.NodeCoordinator",
+ "kwargs": {"bundles_root": ["/opt/airflow/ts-bundles"]}
+ }
+ }
+ queue_to_coordinator = {"typescript": "ts"}
+
+``bundles_root`` is one or more directories the coordinator scans for bundles;
``queue_to_coordinator``
+routes stub tasks with ``queue="typescript"`` to this coordinator. See
+:ref:`typescript-sdk/coordinator-config` for the full list of accepted
``kwargs``.
+
+There is no separate Node.js worker to run: the Airflow worker launches the
bundle with ``node`` once per
+task instance.
+
+.. note::
+
+ The coordinator runs inside the Airflow worker, so the ``[sdk]`` config (and
the packed ``bundle.mjs``
+ files in ``bundles_root``) only need to be present wherever tasks actually
execute. With
+ ``CeleryExecutor``, setting them on the Celery workers is sufficient. With
``LocalExecutor``, tasks run
+ inside the scheduler process, so they must be present where the scheduler
can read them. The API server
+ and Dag processor do not need them.
+
+Writing tasks
+-------------
+
+Every task handler receives a single ``TaskHandlerArgs`` object:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 15 85
+
+ * - Field
+ - Value
+ * - ``ctx``
+ - The task's execution context: ``dagId``, ``taskId`` (including any
TaskGroup prefix), ``runId``,
+ ``tryNumber``, ``mapIndex`` (``-1`` for an unmapped task), and
``signal`` — an ``AbortSignal`` that
+ fires when Airflow terminates the task. Pass ``signal`` to ``fetch()``,
timers, or other APIs that
+ accept an ``AbortSignal`` for cooperative cancellation.
+ * - ``client``
+ - A ``TaskClient`` for Airflow Variables, Connections, and XCom.
+
+A non-``undefined`` return value becomes the task's ``return_value`` XCom,
matching Python ``@task``
+behavior. An uncaught exception (or rejected promise) marks the task instance
failed in Airflow, triggering
+retries if configured on the stub.
+
+The ``TaskClient`` surface
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* ``getVariable(key)`` — returns the Variable as a string, or ``null`` when it
is missing;
+ ``getVariableOrThrow(key)`` throws ``VariableNotFoundError`` instead,
matching Python ``Variable.get``
+ with no default.
+* ``getConnection(connId)`` — returns a ``ConnectionResult`` with fields
``id`` and ``type``, plus the
+ optional fields ``host``, ``schema``, ``login``, ``password``, ``port``, and
``extra`` (each may be
+ missing or ``null``), or ``null`` when the connection does not exist.
+* ``getXCom<T>({key, ...})`` — reads an XCom value, or ``null`` when it is
missing. The locator fields
+ (``dagId``, ``runId``, ``taskId``, ``mapIndex``) default to the current
task; pass ``taskId`` to read an
+ upstream task's XCom. See :ref:`typescript-sdk/types` for how the stored
JSON maps to JavaScript types.
+* ``setXCom({key, value, ...})`` — publishes an XCom value.
+
+Logging
+-------
+
+Anything the task writes to stdout or stderr (``console.log``,
``console.error``) is captured by the worker
+and shown in the Airflow task log (stdout at ``INFO`` level, stderr at
``ERROR`` level). The SDK does not
+yet expose a dedicated structured-logging API.
+
+.. _typescript-sdk/types:
+
+XCom type mapping
+-----------------
+
+XCom values are stored as JSON in Airflow's metadata database. The table below
shows how those JSON types
+surface as JavaScript values when read back via ``getXCom``.
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 35 40
+
+ * - Python type
+ - JSON
+ - JavaScript type (from ``getXCom``)
+ * - ``int``
+ - number (integer)
+ - ``number`` (see note)
+ * - ``float``
+ - number (decimal)
+ - ``number``
+ * - ``str``
+ - string
+ - ``string``
+ * - ``bool``
+ - boolean
+ - ``boolean``
+ * - ``None``
+ - null
+ - ``null``
+ * - ``list``
+ - array
+ - ``Array``
+ * - ``dict``
+ - object
+ - ``object``
+
+.. note::
+
+ JavaScript has a single ``number`` type (an IEEE 754 double), so integers
and decimals arrive as the same
+ type, and integers larger than ``Number.MAX_SAFE_INTEGER`` (2\ :sup:`53` −
1) may lose precision.
+
+.. _typescript-sdk/build:
+
+Building and packaging
+----------------------
+
+``airflow-ts-pack`` (shipped with the SDK) bundles the entry module and all of
its imports with esbuild into
+a single self-contained ESM file, ``bundle.mjs``, and embeds the manifest (the
``dag_id`` and ``task_id``
+map plus the supervisor schema version) as a leading ``//#
airflowMetadata=<base64>`` comment — one file to
+deploy, with no separate manifest or ``node_modules``.
+
+.. code-block:: bash
+
+ npx airflow-ts-pack src/main.ts --outdir dist
+
+Use ``--outdir <dir>`` to choose the output directory (default ``dist``) and
``--source <name>`` to set the
+source name displayed in the Airflow UI (default: the entry file's basename).
+
+Deploying
+~~~~~~~~~
+
+Copy or mount ``bundle.mjs`` into a directory listed in the coordinator's
``bundles_root``.
+:class:`~airflow.sdk.coordinators.node.NodeCoordinator` searches the
configured directories in order and
+launches the first usable bundle with ``node``.
+
+.. _typescript-sdk/coordinator-config:
+
+:class:`~airflow.sdk.coordinators.node.NodeCoordinator` configuration
+---------------------------------------------------------------------
+
+All ``kwargs`` in the ``coordinators`` config entry are passed to the
+:class:`~airflow.sdk.coordinators.node.NodeCoordinator` constructor:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 30 15 55
+
+ * - Parameter
+ - Default
+ - Description
+ * - ``bundles_root``
+ - *(required)*
+ - One or more directories searched, in order, for a ``bundle.mjs`` (with
embedded metadata, or with an
+ ``airflow-metadata.yaml`` sidecar). Accepts a string, a path, or a list
of strings/paths.
+ * - ``node_executable``
+ - ``"node"``
+ - Path to the ``node`` binary. Defaults to ``node`` on ``$PATH``.
+ * - ``task_startup_timeout``
+ - ``10.0``
+ - Seconds to wait for the Node.js subprocess to connect after launch.
Increase this if your bundle
+ startup is slow (e.g. on constrained hardware).
+
+Limitations
+-----------
+
+* **A Python stub Dag is still required.** The Execution API does not yet
carry Dag structure for non-Python
+ languages, so task names and dependencies are declared in Python with
+ :func:`@task.stub <airflow.sdk.task.stub>`.
+* **Alpha status.** The SDK API may change in incompatible ways between
releases.
+* **One bundle per coordinator.**
:class:`~airflow.sdk.coordinators.node.NodeCoordinator` launches the first
+ usable bundle found in ``bundles_root``; it does not yet route different
Dags or tasks to different
+ bundles. To serve multiple bundles, register multiple coordinators on
separate queues.
+* **One Node.js subprocess per task instance.** Tasks that need to share
in-process state between instances
+ should use XCom or an external store instead.
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 1fe86d21ed4..36baf354aa6 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -600,7 +600,9 @@ EOL
equest
errno
errored
+esbuild
eslint
+ESM
ETag
etag
etl
diff --git a/task-sdk/docs/api.rst b/task-sdk/docs/api.rst
index 984b0cd7261..51aa275b90b 100644
--- a/task-sdk/docs/api.rst
+++ b/task-sdk/docs/api.rst
@@ -298,6 +298,10 @@ See
:doc:`apache-airflow:authoring-and-scheduling/language-sdks/index` for a con
.. autoapiclass:: airflow.sdk.coordinators.executable.ExecutableCoordinator
+.. rubric:: Node.js (TypeScript)
+
+.. autoapiclass:: airflow.sdk.coordinators.node.NodeCoordinator
+
Execution Time Components
-------------------------
.. rubric:: Context
diff --git a/ts-sdk/README.md b/ts-sdk/README.md
index 9f2d0c13442..2ad2eff9e4e 100644
--- a/ts-sdk/README.md
+++ b/ts-sdk/README.md
@@ -26,11 +26,9 @@ Public TypeScript interfaces for writing Apache Airflow task
handlers.
This package defines the user-facing task handler contract and the coordinator
runtime used to execute registered TypeScript handlers from Airflow.
-## Install
-
-```bash
-pnpm add @apache-airflow/ts-sdk
-```
+> **Note:** This package is not yet published to a public npm registry. Until
an
+> official Apache Airflow release is available, build and use it from source
(see
+> [Development](#development)).
## Task Handlers