Outputs

Produce human-friendly structured outputs from your tasks
Reading through logs can be noisy and challenging—Airplane allows tasks to produce explicit outputs separate from logs.
Airplane outputs are displayed in rich, user-friendly tables:

Output data model

Airplane outputs of a task are similar to the return value of a function. Airplane outputs are JSON values and can be primitive values (strings, numbers), lists, or even complex objects.
Different Airplane tasks will return different kinds of outputs. For example, a SQL task will return its SELECT results as a list of objects (which is rendered as a table of values). Node.js tasks can output values however the author desires.

Output rendering in the UI

Airplane's UI does a best effort rendering of output, depending on the output data structure. The output is typically formatted into a table or multiple tables. If the output data structure is an object containing only lists as values, the UI will render each separate list as its own table, with the table name set to the key of the list. Otherwise, the entire output will be rendered as one table.
Primitive values (strings, numbers, booleans) are rendered as a single cell in a table.
Lists of primitive values are rendered as a single column table with multiple rows.
Lists of objects are rendered as a table, one row per item, and one column per key. For example, [{"id": 1, "name": "John"}, {"id": 2, "name": "Sally"}] will render as two rows with two columns, "id" and "name".
Nested objects are rendered as collapsed values inside the table.

Producing outputs from tasks

In most task types (Node.js, Python), you can produce a basic output from a task by returning directly from the main function.
javascript
Copied
1
export default async function (params) {
2
return { id: 123, name: "abc" };
3
}
An output is typically any value that can be encoded as JSON.
For task types that are not based on a main function, Airplane captures task output by filtering stdout lines that start with certain keywords. This protocol is described in the advanced section below.

SDK output methods

If more complicated output structures are needed, the Airplane SDK provides functions for modifying the output incrementally over the course of the task.

Set output

You can set the entire output object by calling the appropriate SDK function:
javascript
Copied
1
import airplane from "airplane";
2
3
export default async function (params) {
4
airplane.setOutput("ABC");
5
airplane.setOutput("DEF");
6
// The final output value will be "DEF".
7
}
Note that returning a value from a main function is equivalent to calling the corresponding set output function at the end of task execution.

Append to output

If your output is intended to be an array, instead of setting the entire output to be an array, you can use the following:
javascript
Copied
1
import airplane from "airplane";
2
3
export default async function (params) {
4
airplane.appendOutput({ id: 1, name: "abc" });
5
airplane.appendOutput({ id: 2, name: "def" });
6
}
The resulting output will be [{id: 1, name: "abc"}, {id: 2, name: "def"}].
During visualization, if a task outputs a list of objects, the values are collected into a table with a column per key:
javascript
Copied
1
airplane.appendOutput({ name: "Alabama", capital: "Montgomery" });
2
airplane.appendOutput({ name: "Alaska", capital: "Juneau" });
3
airplane.appendOutput({ name: "Arizona", capital: "Phoenix" });

Common use cases

Displaying user messages

By default, users see if a run has succeeded or failed. You can output messages as you make progress (outputs are shown as soon as they're received), and you can also output more detailed error messages if something fails.

Exporting data

You can use Airplane outputs for lightweight reporting. For example, you can query a database for a set of data, output each row as a JSON object, and allow users a quick way to see data (and download it as a CSV).

Advanced

Log output protocol

Under the hood, Airplane captures task output by filtering stdout lines that start with either airplane_output_set or airplane_output_append. In other words, returning from a main function, or calling any of the SDK output functions, generates an airplane_output_set or airplane_output_append line in stdout, which will be parsed by Airplane to obtain the final task output.
Examples of such log lines are provided below:
Copied
1
airplane_output_set {id: 1, name: "abc"}
For languages that do not have a SDK provided (e.g. Docker image, shell), printing such log lines to stdout is the recommended method for generating outputs to a task.

JSON paths

Setting and appending can also apply to a subset of the output instead of overwriting the entire output or appending to the main array. The airplane output set and airplane output append commands have an optional path parameter, which allows part of an existing output to be edited.
Copied
1
airplane_output_set {"k1": "v1", "k2": {"nested": "abc"}, "k3": [1, 2, 3]}
2
airplane_output_set:k1 "new value"
3
4
# Output is now: {"k1": "new value", "k2": {"nested": "abc"}, "k3": [1, 2, 3]}
5
6
# Note that these are all valid path syntaxes.
7
airplane_output_set:k2.nested "def"
8
airplane_output_set:k2["nested"] "def"
9
airplane_output_set:["k2"]["nested"] "def"
10
airplane_output_set:["k2"].nested "def"
11
12
# Output is now: {"k1": "new value", "k2": {"nested": "def"}, "k3": [1, 2, 3]}
13
14
airplane_output_set:k3[2] 4
15
16
# Output is now: {"k1": "new value", "k2": {"nested": "def"}, "k3": [1, 2, 4]}
17
18
# Append also accepts paths.
19
airplane_output_append:k3 8
20
21
# Output is now: {"k1": "new value", "k2": {"nested": "def"}, "k3": [1, 2, 4, 8]}
22
23
# If a set or append command encounters a null or undefined value along the
24
# path to the final output location, it automatically generates an empty object
25
# in the output, for convenience.
26
airplane_output_set:k4.new 5
27
28
# Output is now: {"k1": "new value", "k2": {"nested": "def"}, "k3": [1, 2, 4, 8], "k4": {"new": 5}}
The set/append output functions in the SDKs will help to automatically construct the JSON path for you, from an array of path components. Refer to the documentation of each individual SDK for the correct format.

Handling large outputs

Due to limitations of Docker/Kubernetes, output lines in stdout that are too long (greater than approximately 16000 characters) may not be parsed correctly, so we provide a "chunking" mechanism to split up such lines.
If you are using a SDK, this chunking is automatically handled for you.
Copied
1
airplane_chunk:key1 airplane_outp
2
airplane_chunk:key1 ut_set "Hello World!"
3
airplane_chunk_end:key1
4
5
airplane_chunk:key2 airplane_outp
6
airplane_chunk:key2 ut_set "How ar
7
airplane_chunk:key2 e you?"
8
airplane_chunk_end:key2
9
10
(is equivalent to)
11
12
airplane_output_set "Hello World!"
13
airplane_output_set "How are you?"