Output

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

Output data model

Airplane output from a task is similar to the return value of a function. Airplane output can be any JSON value such as primitive values (strings, numbers), lists, or even complex objects.
Different Airplane tasks will return different kinds of output. For example, a SQL task will return its SELECT results as a list of objects (which is rendered as a table of values). JavaScript 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 output from tasks

Non-code tasks (SQL, REST) produce outputs automatically e.g. from the result of a SQL SELECT query or REST GET request.
For most code tasks (JavaScript, Python), the easiest way to produce output from a task is by returning a value from the main function.
typescript
Copied
1
export default async function (params) {
2
return { id: 123, name: "abc" };
3
}
For code tasks that are not based on a main function (Shell, Docker), Airplane captures task output by filtering stdout lines that start with certain keywords.
For example, in a shell task you can produce output by echoing a string that starts with airplane_output_set.
shell
Copied
1
#!/bin/bash
2
3
data='{"id": 123, "name": "abc"}'
4
echo "airplane_output_set ${data}"
For more information about creating task outputs via stdout, see 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:
typescript
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:
typescript
Copied
1
import airplane from "airplane";
2
3
export default async function (params) {
4
airplane.appendOutput({ name: "Alabama", capital: "Montgomery" });
5
airplane.appendOutput({ name: "Alaska", capital: "Juneau" });
6
airplane.appendOutput({ name: "Arizona", capital: "Phoenix" });
7
}
The resulting output will be [{"name": "Alabama", "capital": "Montgomery"}, {"name": "Alaska", "capital": "Juneau"}, {"name": "Arizona", "capital": "Phoenix"}] which will be displayed in the UI as:

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 (output is shown as soon as it is received), and you can also output more detailed error messages if something fails.

Exporting data

You can use Airplane output 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 output from a task.
To produce an output that is a string, ensure the string value is wrapped in double quotes.
Copied
1
// This will work
2
airplane_output_set "value"
3
4
// This will not work
5
airplane_output_set value

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 output

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?"
However, note that there is still a maximum possible size set on output lines, even with chunking applied. If the size of all combined output line chunks is greater than 4MiB, the output will not be processed, and a warning message will be included in the run logs instead. Note that this does not affect any other part of the run (i.e. the run still can succeed) - only the output will be affected.

Files

Files can be directly returned from a task, and will be displayed in the UI as a downloadable link. If the file is a text, csv, image, video, or audio file, it will also be rendered inline in the UI.
typescript
Copied
1
import airplane from "airplane";
2
3
export default airplane.task(
4
{
5
slug: "return_image",
6
parameters: {
7
image_upload: "upload",
8
},
9
},
10
async (params) => {
11
return params.image_upload;
12
},
13
);