KEPLIN Docs

Datastores and data

How a screen loads, filters and saves data — record and list datastores, keys, filters, paging and the data bindings.

A screen does not talk to the database directly: it talks to datastores — the screen's data containers, which load records through the app's table APIs. The widgets bind to the datastores: a Table shows the rows of a list datastore, a form's fields read from and write to a record datastore.

This is the link between two chapters: the table APIs are created over the data model (the APIs & GraphQL chapter); here the screen is bound to them.

The two kinds of datastore

Kind What it loads What for
Record ONE record (or a new, empty record) Forms: the fields bind to the record's fields, and at the end it is saved.
List A collection of records Tables, lists, cards, charts, kanbans, calendars.

Datastores can live in two places:

  • On the screen — created in the inspector with nothing selected, in the Data category. They are shared: several widgets can read from the same one, and it is what you use for forms and for master–detail relationships.
  • Inside a widget — the data widgets (Table, Chart, KPI…) have their own datastore in their Data category. It is the most common case for independent grids and charts.

The engine is the same in both places; the only difference is in the value origins available in the filters (see the bindings).

Creating a datastore on the screen

  1. Click an empty area of the canvas so the inspector shows the screen.
  2. In the Data category, click + record or + list.
  3. Click the created datastore to open the Configure datastore modal.
  4. Give it a Datastore name — it is by this name that the widgets and the code find it (e.g. conta, contas).
  5. Under Table API, choose the API that serves the data. The API's fields become available for columns, bindings and filters.

The Data category of the Ficha de Conta screen: the record datastore, the list one and the + record / + list buttons.
The Data category of the Ficha de Conta screen: the record datastore, the list one and the + record / + list buttons.

Nota

With no published table APIs, the selector warns: No published table APIs in this app. Create the API over the model's entity first — it is a step of the APIs & GraphQL chapter.

Record datastore — which record to load

A record datastore answers one question: which record? The answer is given in Which record to load (key):

  1. Click + key field.
  2. Choose the field (by default, the primary key), the operator and the value — typically a Param of the route: the Ficha de Conta screen receives id in the address and loads the account with that id.
  3. Several conditions form a composite key — all have to match.

With no conditions, the datastore loads a new (empty) record — that is how the same form screen serves for creating: opened without an id, it starts blank; saved, it does the insert.

List datastore — filters and loading

Filters (where)

The Filters (where) are conditions applied whenever the data is read — this is where you limit what comes from the database. Each condition is field / operator / value; + add filter adds conditions and + group creates nested sub-groups, with All (AND) or Any (OR) deciding how they combine.

An example from Gestão de Clientes: the Contas screen filters estado eq "activa"; the "my accounts" panel adds gestor eqSessionusername.

Loading and page

Option What it does
Load everything Brings every record of the filter at once — changing page, sorting and filtering on the screen become instant.
One page at a time Goes to the server on every page change — for large tables, where bringing everything makes no sense.
Per page How many rows are shown at a time on the screen — not to be confused with how many records are read.
Load automatically Read the data as soon as the screen opens. Turn it off if you prefer to load only after an action (a "Search" button, for example).

Dica

In either mode, use the Filters (where) to limit what is read. "Load everything" with a decent filter is fast; with no filter at all, it is asking for the whole table.

The bindings — where a value comes from

Whenever a filter, a key or a property needs a value, you use the same piece: the binding. The first selector says the origin; the rest changes with it:

Origin What it is
Literal A value typed right there, the same for everyone.
Param A parameter of the screen's route (Route parameters section).
Session A field of the signed-in user (userId, username, name).
State A value kept in the app's memory with keplin.state.set() — available on every screen.
Datastore A field of another datastore of the screen — the basis of master–detail.
Widget The current value of another input widget — the basis of interactive filters.

The Datastore and Widget origins only exist on the datastores inside widgets — they depend on the rest of the screen. On the screen's datastores you get the first four.

With these pieces the everyday patterns are assembled without code:

  • Master–detail — the account's opportunities table: on the table's datastore, filter contaId eqDatastorecontaid. Selecting another account reloads the detail.
  • Filter by text — a "search" Textbox and, on the table's datastore, nome containsWidget ▸ the box. (To filter only when a button is clicked, it is done by event — see Events and the SDK.)

Binding form fields to a record

Each form field has, in the Data category, the Data binding section: choose the record datastore and the field. From then on the input shows the loaded value and the changes stay in the datastore — unsaved — until someone saves.

The final step is a button whose event saves:

const ok = await keplin.data.store("conta").save();
if (ok) {
  keplin.ui.toast("Gravado.", "success");
}

This code is exactly what the pre-defined Save datastore action of the event editor inserts for you. save() validates first (required fields, rules, validation scripts) and only saves if everything passes; it returns true if it saved.

The Ficha de Conta screen: form fields bound to the record datastore, ready to save.
The Ficha de Conta screen: form fields bound to the record datastore, ready to save.

The Configure datastore modal, field by field

The Configure datastore modal: API, key/filters, loading and page.
The Configure datastore modal: API, key/filters, loading and page.

Field Record List
Datastore name
Table API
Which record to load (key)
Filters (where)
Loading / Per page
Load automatically

Datastores on public screens

On a screen marked Public screen (no session), the data comes only from APIs with public read: the selector only shows those, and an already chosen API that is not public gets flagged — This API has no public read — on a session-less screen it won't load data. The read is marked as public in the API editor.

The data in code

Everything the datastores do is also in the events' SDK — keplin.data.store("nome") returns the datastore by name, with reload(), setWhere(), get()/set()/save() and company. The chapter Events and the SDK walks through it.

Why doesn't…?

  • Why doesn't it load data? Check, in order: is Load automatically on? Does the chosen API exist and is it published? On a public screen, is the API's read public? Is the filter not excluding everything?
  • Why does it always open an empty record? The record datastore has no conditions in Which record to load (key) — or the parameter used in the condition is not arriving in the route.
  • Why did I change the model and the new column does not show up? The datastore keeps a snapshot of the API's fields from when you chose it. Reopen Configure datastore and choose the API again to refresh the snapshot.
  • Why is the paging slow? You are on One page at a time with many trips to the server — or on Load everything with no filters on an enormous table. Adjust the mode to the real size of the data.