Tables
A table in Flow is a rectangle of rows and columns that came out of a CSV file or an Excel sheet. Fourteen nodes can read it, filter it, join it, group it and write it back to a file. There is no database involved: the table is computed inside a single run, in memory, and disappears together with it.
The nodes themselves are available on the free plan, but routine processing of exports is not: schedule and webhook triggers only work on a paid plan, and on the free plan the whole run has to fit into 120 seconds while a single uploaded file cannot exceed 10 MB. Exact values for both plans are in the limits.
The “table” port
Ports on df nodes declare the dataframe data type. An edge carries
the whole table at once, not row after row: grouping, sorting and joining are defined
over the entire set of rows, so a “stream of rows” simply would not fit — the node would
still have to wait for the last row to arrive.
Two consequences follow immediately.
A table does not let you walk over rows. To process every row separately — say, to send one email per row — first turn the table into a list of objects with the convert node, and feed that list to a loop.
The log holds a preview, not the table. The live table exists only in the run’s memory;
what goes into the run history, the SSE stream and the Output
panel is a snapshot: column names, types, a size like 12480×7 and the first 50 rows. This
keeps a hundred-megabyte table from being copied into the database at every step —
see timeouts and budgets.
Where the files come from
From the workspace file storage — the same one that holds chat attachments and everything you uploaded by hand on the Files tab. By default df nodes read and write the storage of their own workspace, and the storage port is not even drawn: the configuration only needs a path and, if required, a subfolder. The port appears only when you switch the connection source to external — then a storage node is placed next to it, and the same graph starts working against someone else’s disk over WebDAV.
The path is a template, so reports/{{ variables.session_id }}.csv gives you one file per
session.
The pipeline
- Execute
- Execute + Data
Reader nodes are started by a regular execute edge, while for all the others the incoming table doubles as the trigger: the table arrived, the node ran. That is why the pipeline looks like a chain without a single branch, and the order of steps in it is literally the order of operations.
The last node in the chain returns not a table but a write report: the path, how many bytes and how many rows were written. It is convenient to hand it to exit or to substitute it into the answer text.
All fourteen nodes
| Node | What it does | What it returns |
|---|---|---|
| df_read_csv | Reads CSV from storage: delimiter, encoding, header row number, row limit | A table |
| df_read_xlsx | Reads a sheet of an xlsx workbook — by name or by index (0 = first) | A table |
| df_query | Keeps the rows matching an expression: revenue > 1000 and country == 'DE' | A table |
| df_select | Picks, renames and drops columns | A table |
| df_sort | Sorts by one or several columns, each ascending or descending | A table |
| df_groupby_agg | Groups by columns and computes aggregates: sum, mean, min, max, count, nunique, first, last, std, median | A table: one row per group |
| df_merge | Joins two tables on shared keys: inner, left, right, outer, cross | A table |
| df_concat | Glues two tables together by rows (one under the other) or by columns (side by side) | A table |
| df_drop_duplicates | Removes duplicates over a set of columns; you can keep the first or the last occurrence | A table |
| df_update | Assigns values to columns in the rows matching a condition; an empty condition means all rows | A table |
| convert | Converts json, csv and dataframe into one another | A value of the chosen type |
| df_write_csv | Writes the table to CSV, overwriting the whole file | Path, bytes, row count |
| df_write_xlsx | Writes the table to an xlsx sheet, overwriting the whole file | Path, bytes, row count |
| dataframe_tool | Gives an agent ten tools over tables | Not a graph step: it plugs into an agent |
The fields of each node, its ports and an example graph are on its reference page.
Conversion: the one explicit way
convert is a graph step with its own timing and its own error,
and that is the point: the conversion is visible on the canvas. Four directions are
registered: json → dataframe, dataframe → json, dataframe → csv, csv → dataframe.
The csv ↔ json pair is not among them — it is assembled from two nodes via dataframe,
and the editor will tell you so before the run rather than halfway through it.
A table and a collection are different things
They are easy to confuse: both look like a table with columns. The difference is lifetime.
- A table lives inside a run. It arrives from a file, goes through a chain of nodes and
disappears when the run ends. The only way to keep it is to write it to a file with the
df_write_csvordf_write_xlsxnode. - A document collection outlives the run. It is workspace storage: the next run sees what the previous one wrote, documents can be selected by fields and edited by hand in the interface.
The rule is simple: a one-off file processing is a table; data that has to still be there tomorrow is a collection. Semantic search over text is a knowledge base altogether.
Boundaries
- File size. Reading and writing have a ceiling in bytes: 25 MiB for reads and 50 MiB for writes by default. The field sits in the Advanced section and can be lowered but not raised — the ceiling is the maximum.
- Time. A node cannot run longer than 600 seconds, and the whole run is capped by the plan (120 seconds on the free one). Parsing a large export hits that before it hits memory; see timeouts and budgets.
- Space. A written file counts towards the owner’s file storage quota — a single quota across all of their workspaces.
- Failures. Unreachable storage, a broken CSV, a missing column — these are node errors,
not an empty table: the run fails unless you drew an
on_errorbranch. How to draw one is in error handling.
Tables in an agent’s hands
dataframe_tool plugs into an agent and gives the model ten tools: load a CSV or an xlsx sheet, inspect the schema and a sample, filter, pick columns, join, group, export to JSON, save to CSV or xlsx. The tables stay on the server — the model gets a short handle and passes it to the next tool instead of hauling the data through its context.