# Data Store

Stores and reads rows in a Yokk table. There is no database to create, no DSN
to paste and no SQL to write: you declare a table and its fields once in
**Production › Data**, and this node reads and writes them.

Unlike **Set state** and **Memory manager**, which only live for the duration of
one run, Data Store rows survive across runs and are shared by everyone in the
workspace.

## Declare the table first

A table is declared in **Production › Data** with a name and a list of fields.
Each field has a type — text, number, yes/no, date, date & time, choice, email
or URL — and can be marked **required** or **unique**. A table can also be
marked **append-only**, which means its rows can only be added and read: they
can never be modified or deleted, which is what makes it usable as an audit log.

The node reads that definition. It never creates a table and never invents a
field.

## Configuration

Everything is visual: pick the table, pick the operation, then map the declared
fields one by one. There is no JSON editor anywhere in this node.

| Operation | What it does |
| --- | --- |
| Insert a row | Always adds a row. Refused if a unique field already holds the value. |
| Insert or update a row | Updates the row whose **unique** fields already hold these values, or creates it. |
| Find rows | Returns the matching rows, newest first by default. |
| Update rows | Changes only the fields you map on the matching rows. |
| Delete rows | Marks the matching rows as deleted — nothing is erased. |
| Count rows | Returns how many rows match, without loading them. |
| Increase a number | Adds an amount to a number field, safely under concurrency. |

Values accept the usual builder expressions, for example
`{event.body.email}`, `{state.customer.id}` or
`{outputs.score.parsed.value}`. Fields declared as a choice or as yes/no get a
picker; the **fx** button next to it switches to an expression.

### Matching an existing row

Insert-or-update does not let you choose the matching fields: the row is
identified by the fields the table declares **unique**, and the database index
enforces it. Map those fields in the values and the node shows you which ones
they are.

### Conditions

Update, Delete, Count and Increase take a list of conditions built from the
declared fields (is, is not, contains, greater than, empty…). Update, Delete
and Increase require at least one condition, so they can never apply to the
whole table by accident.

### Only if the row still has…

Update and Increase accept an optional safety check. The write is applied only
while the row still holds the values you list; otherwise it is refused and
nothing changes. This is what stops two operators from silently overwriting
each other — for example to enforce that a counter can only ever go up.

## Runtime output

The node writes its main result to `last_output` and, when structured, to
`last_output_parsed`. Downstream nodes can reference named outputs through
`{outputs.<node_id>.parsed}`:

- `records` — the rows returned by Find, or the rows changed by Update
- `record` — the single row written by Insert, Insert-or-update, or Increase
- `count` — the number of matching or affected rows
- `created` — for Insert or update, whether a new row was created

## When it refuses

The node reports what happened in plain language instead of a technical trace:

- the workspace has reached its row or storage quota, with the limit reached
- a unique field already holds that value
- the table is append-only, so the update or deletion is refused
- the safety check no longer matches, so the write was not applied

## Example workflow

Use the workflow card below to create a working example in the editor. Open
each Data Store node and pick your own table — the field mapping is then read
from its definition.

---

*Source: https://agentbuilder.systalink.sn/docs/node-data-store — human documentation.*
*Other language: [/docs-md/fr/node-data-store.md](/docs-md/fr/node-data-store.md).*
*Machine-readable index: [/llms.txt](/llms.txt).*
