Views
Top-level view blocks that compose streams into a spreadsheet, with computed columns that run a graph for each row.
A view presents stream records as a typed table. It names its source streams, declares a row schema, and maps each source row with a columns block. Optional computed columns run a workflow once per row to enrich or look up data.
Views materialize on Swirls Cloud. Browse them in the dashboard or read them through the API. .swirls workflows cannot read directly from a view.
Top-level view block
| Field | Type | Required | Description |
|---|---|---|---|
label | string | No | Display name. Defaults to the view's name. |
description | string | No | Description. |
streams | list | Yes | Stream block names this view composes. Their rows become the view's rows. |
schema | @json { } or named schema | Yes | JSON Schema for one mapped (non-computed) row. |
columns | @ts { } | Yes | Maps one source stream row into the row shape (validated against schema). |
computed | block | No | Named computed columns, each running a graph per row. |
enabled | boolean | No | When false, the view is not materialized. |
Each computed column (inside computed { }):
| Field | Type | Required | Description |
|---|---|---|---|
graph | identifier | Yes | Workflow run once per row to compute this column. |
input | @ts { } | Yes | Maps the row into the graph's trigger input (returns an object). |
output | @ts { } | No | Maps the graph result into the cell value. Omit to store the graph output directly. |
computed is a block, not a key: value field — write computed { ... } with no colon, the same way versions { } is written inside a stream block.
view topic_dashboard {
label: "Topic dashboard"
description: "One row per stored topic, enriched with sentiment"
streams: [store_topic_tokens]
schema: @json {
{
"type": "object",
"required": ["topic", "tokens"],
"properties": {
"topic": { "type": "string" },
"tokens": { "type": "number" }
}
}
}
columns: @ts {
return {
topic: context.streams.store_topic_tokens.output.topic,
tokens: context.streams.store_topic_tokens.output.tokens
}
}
computed {
sentiment {
graph: enrich_topic
input: @ts {
return { topic: context.row.topic }
}
output: @ts {
return context.output.root.sentiment
}
}
}
}How rows are built
A view has one row per source stream row.
-
columnsruns once per source row. The originating row is available ascontext.streams.<streamName>.output(andcontext.streams.<streamName>.<version>.output, e.g..v1.output). Only the stream that produced the current row is present. Return the mapped row object — it is validated against the viewschema, exactly like a stream'sprepareis validated against its version schema. -
Computed columns each run a workflow once per row:
inputreceivescontext.row(the mapped row fromcolumns) plus the samecontext.streams.<name>. Return the object passed to the graph as its trigger input.outputreceivescontext.output(the completed graph's leaf node outputs, keyed by leaf node name, the same shape a streampreparesees) pluscontext.row. Return the cell value. Withoutoutput, the cell value is the graph's output directly.
Computed columns run real workflows
Every computed cell is a workflow execution. A view over a busy stream with computed columns can launch a large number of runs, so add computed columns deliberately.
- Cells move through
pending → running → completed → failed. The spreadsheet shows a loading state until each cell settles. - Computed-column runs are billed against
execution_credits, the same as workflows started by a trigger. When the org is over quota, the cell settles asfailedrather than running for free. - New rows materialize as the source workflow completes. Deploying a view backfills existing stream rows.
- Recompute is available from the cloud UI. It fills gaps: it materializes stream rows the view missed and their cells. It does not re-run cells that already settled.
Avoid execution loops
A computed column must not run a graph that writes back into a stream the same view composes. That would make every materialized row enqueue another graph run, which writes another row, forever.
The validator rejects this at deploy time:
View "X" creates an infinite execution loop: a computed column runs a graph that writes a stream this view (or a view it feeds) composes.
It catches both the direct case and chains across multiple views. Point computed columns at graphs that enrich or look things up — not at graphs that append to the view's own source streams.
Validation rules
- View names must match
^[a-zA-Z0-9_]+$; duplicate names error. streamsis required and non-empty; each entry must name a declared stream, and listing a stream twice errors.schemaandcolumnsare required;columnsmust be a non-empty@tsblock.- Each computed column needs a
graphnaming a declared workflow and a non-emptyinput; an emptyoutputerrors; duplicate computed column names error. - Execution-loop cycles are rejected (see above).