Datastores y datos
Cómo una pantalla carga, filtra y guarda datos — datastores de registro y de lista, claves, filtros, paginación y las conexiones a datos.
Una pantalla no habla directamente con la base de datos: habla con datastores — contenedores de datos de la pantalla que cargan registros a través de las APIs de tabla de la app. Los widgets se conectan a los datastores: una Tabla muestra las filas de un datastore de lista, los campos de un formulario leen y escriben en un datastore de registro.
Este es el eslabón entre dos capítulos: las APIs de tabla se crean sobre el modelo de datos (capítulo APIs & GraphQL); aquí se conecta la pantalla a ellas.
Los dos tipos de datastore
| Tipo | Qué carga | Para qué |
|---|---|---|
| Registro | UN registro (o un registro nuevo, vacío) | Formularios: los campos se conectan a los campos del registro, y al final se guarda. |
| Lista | Una colección de registros | Tablas, listas, tarjetas, gráficos, kanbans, calendarios. |
Los datastores pueden vivir en dos sitios:
- En la pantalla — creados en el inspector sin selección, en la categoría Datos. Son compartidos: varios widgets pueden leer del mismo, y es lo que se usa para formularios y para relaciones maestro-detalle.
- Dentro de un widget — los widgets de datos (Tabla, Gráfico, KPI…) tienen su propio datastore en su categoría Datos. Es el caso más común para cuadrículas y gráficos independientes.
El motor es el mismo en los dos sitios; la única diferencia está en los orígenes de valor disponibles en los filtros (ver las conexiones).
Crear un datastore en la pantalla
- Haz clic en una zona vacía del canvas para que el inspector muestre la pantalla.
- En la categoría Datos, haz clic en + registro o + lista.
- Haz clic en el datastore creado para abrir el modal Configurar datastore.
- Dale un Nombre del datastore — por este nombre los widgets y el
código lo encuentran (ej.:
conta,contas). - En API, elige la API que sirve los datos. Los campos de la API quedan disponibles para columnas, conexiones y filtros. En un datastore de lista aparecen también las APIs de pipeline, con la insignia pipeline: devuelven la lista entera, sin filtros ni paginación.

Nota
Sin APIs de tabla publicadas, el selector avisa: Sin APIs de tabla publicadas en esta app. Crea primero la API sobre la entidad del modelo — es un paso del capítulo APIs & GraphQL.
Datastore de registro — qué registro cargar
Un datastore de registro responde a una pregunta: ¿cuál registro? La respuesta se da en Qué registro cargar (clave):
- Haz clic en + campo de la clave.
- Elige el campo (por defecto, la clave primaria), el operador y el valor
— típicamente un Param de la ruta: la pantalla Ficha de Conta
recibe
iden la dirección y carga la cuenta con ese id. - Varias condiciones forman una clave compuesta — todas tienen que coincidir.
Sin condiciones, el datastore carga un registro nuevo (vacío) — así es
como la misma pantalla de formulario sirve para crear: abierta sin id,
empieza en blanco; guardada, hace la inserción.
Con condiciones que no encuentran ningún registro (un id que ya no existe, o
que queda fuera del alcance del rol de quien abre), la app avisa de que el
registro pedido no existe y el formulario no guarda. Solo una clave escrita en
un campo de la pantalla (una clave natural, como un código de artículo) sigue
siendo la de un registro nuevo.
Datastore de lista — filtros y carga
Filtros (where)
Los Filtros (where) son condiciones aplicadas siempre que los datos se leen — aquí es donde se limita lo que viene de la base de datos. Cada condición es campo / operador / valor; + añadir filtro añade condiciones y + grupo crea subgrupos anidados, con Todas (AND) o Cualquiera (OR) decidiendo cómo se combinan.
Ejemplo de la Gestión de Clientes: la pantalla Contas filtra
estado eq "activa"; el panel «mis cuentas» añade gestor eq →
Sesión ▸ username.
Los operadores:
| Operador | Qué compara |
|---|---|
eq / neq |
Igual / distinto del valor. neq incluye los registros con el campo vacío. |
contains, startsWith, endsWith |
Texto que contiene, empieza o termina por el valor. |
gt, gte, lt, lte |
Mayor, mayor o igual, menor, menor o igual — números y fechas. |
in / nin |
Cualquiera de / ninguno de una lista de valores. El valor es una lista: varios valores separados por comas, o el de una Lista con Selección múltiple vinculada por Widget — así se filtra una tabla por varios centros o varios estados a la vez. nin incluye los registros con el campo vacío. |
Una condición cuyo valor está vacío (la caja de búsqueda en blanco, la lista sin elección) no filtra nada — la pantalla muestra todo hasta que la persona elige.
Cada valor de un filtro se convierte según el tipo de la columna: en una columna de texto, un NIF o un código postal («0012») siguen siendo texto, y en una columna decimal «12,5» es un número.
Carga y página
| Opción | Qué hace |
|---|---|
| Cargar todo | Trae todos los registros del filtro de una vez — cambiar de página, ordenar y filtrar en la pantalla queda instantáneo. |
| Una página cada vez | Va al servidor con cada cambio de página — para tablas grandes, donde traerlo todo no tiene sentido. |
| Por página | Cuántas filas se ven cada vez en pantalla — no confundir con cuántos registros se leen. |
| Cargar automáticamente | Leer los datos en cuanto la pantalla se abre. Desactívalo si prefieres cargar solo tras una acción (un botón «Buscar», por ejemplo). |
Sin Cargar automáticamente, la lista lee cuando alguien se lo pide: un
reload() (en un botón «Buscar», por ejemplo), un cambio de página o de orden,
o un filtro aplicado. Cambiar un campo de la pantalla no la hace leer sola.
Cuando el filtro efectivo cambia (un campo de la pantalla, un parámetro, el estado de la app), la lista vuelve a la primera página. Si la página en la que estabas dejó de existir (borraste el último registro de la última página, por ejemplo), la lista pasa a la última que existe.
Consejo
En cualquiera de los modos, usa los Filtros (where) para limitar lo que se lee. «Cargar todo» con un filtro decente es rápido; sin ningún filtro, es pedir la tabla entera.
Las conexiones — de dónde viene un valor
Siempre que un filtro, una clave o una propiedad necesita un valor, usas la misma pieza: la conexión. El primer selector dice el origen; el resto cambia según él:
| Origen | Qué es |
|---|---|
| Fijo | Un valor escrito ahí mismo, igual para todos. |
| Param | Un parámetro de la ruta de la pantalla (sección Parámetros de ruta). |
| Sesión | Un campo del usuario con sesión iniciada (userId, username, name). |
| Estado | Un valor guardado en la memoria de la app con keplin.state.set() — disponible en todas las pantallas. |
| Datastore | Un campo de otro datastore de la pantalla — la base del maestro-detalle. |
| Widget | El valor actual de otro widget de input — la base de los filtros interactivos. |
Un campo también escribe en el estado: en el Vínculo de datos del campo, elige Estado y escribe la Clave. Un filtro con el origen Estado y la misma clave vuelve a leer los datos cuando el valor cambia. Así es como un área de widgets de la barra filtra las páginas; consulta Navegación de la app.
Los orígenes Datastore y Widget solo existen en los datastores dentro de widgets — dependen del resto de la pantalla. En los datastores de la pantalla quedan los cuatro primeros.
Con estas piezas se montan los patrones del día a día sin código:
- Maestro-detalle — la tabla de oportunidades de la cuenta: en el
datastore de la tabla, filtro
contaId eq→ Datastore ▸conta▸id. Seleccionar otra cuenta recarga el detalle. - Filtro por texto — una Caja de texto «buscar» y, en el datastore de
la tabla,
nome contains→ Widget ▸ la caja. (Para filtrar solo al hacer clic en un botón, se hace por evento — ver Eventos y el SDK.)
Conectar campos de formulario a un registro
Cada campo de formulario tiene, en la categoría Datos, la sección Conexión a datos: elige el datastore de registro y el campo. A partir de ahí el input muestra el valor cargado y los cambios quedan en el datastore — por guardar — hasta que alguien guarde.
El paso final es un botón cuyo evento guarda:
const ok = await keplin.data.store("conta").save();
if (ok) {
keplin.ui.toast("Gravado.", "success");
}
Este código es exactamente lo que la acción predefinida Guardar
datastore del editor de eventos inserta por ti. save() valida primero
(obligatorios, reglas, scripts de validación) y solo guarda si todo pasa;
devuelve true si guardó.
Al guardar un registro que ya existía, solo se envían los campos que cambiaron desde que se leyó. Un campo borrado se guarda vacío (nulo en la base de datos), y en un campo decimal «12,5» se lee como número.

Con cambios sin guardar, salir de la página pide confirmación: por los
menús, por el botón Atrás del navegador o al cerrar la pestaña. Un modal ya
preguntaba al cerrarse. La navegación hecha por código (keplin.nav.go) no
pregunta: quien la llama ya ha decidido, y muchas veces acaba de guardar.
Al guardar un registro nuevo, un campo que la pantalla no muestra no se envía:
en una columna con valor por omisión en la base de datos, o generada por ella,
queda ese valor; una columna obligatoria sin valor por omisión tiene que estar
en la pantalla, y el formulario dice cuál falta. Una clave escrita en un campo
de la pantalla (una clave natural, como un código de artículo) es la de un
registro nuevo; puesta por código con set(), sigue siendo la de un registro
que cambiar. Guardar un registro que entretanto dejó de existir da error, y no
«Guardado».
El modal Configurar datastore, campo a campo

| Campo | Registro | Lista |
|---|---|---|
| Nombre del datastore | ✓ | ✓ |
| API | ✓ | ✓ |
| Qué registro cargar (clave) | ✓ | — |
| Filtros (where) | — | ✓ |
| Carga / Por página | — | ✓ |
| Cargar automáticamente | ✓ | ✓ |
Datastores en pantallas públicas
En una pantalla marcada Pantalla pública (sin sesión), los datos vienen solo de APIs con lectura pública: el selector solo muestra esas, y una API ya elegida que no sea pública queda señalada — Esta API no tiene lectura pública — en una pantalla sin sesión no carga datos. La lectura se marca como pública en el editor de la API.
Los datos en el código
Todo lo que los datastores hacen está también en el SDK de los eventos —
keplin.data.store("nombre") devuelve el datastore por el nombre, con
reload(), setWhere(), get()/set()/save() y compañía. El capítulo
Eventos y el SDK lo recorre.
¿Por qué no…?
- ¿Por qué no carga datos? Mira, por orden: ¿Cargar automáticamente está activado? ¿La API elegida existe y está publicada? En una pantalla pública, ¿la lectura de la API es pública? ¿El filtro no está excluyendo todo?
- ¿Por qué se abre siempre un registro vacío? El datastore de registro no tiene condiciones en Qué registro cargar (clave) — o el parámetro usado en la condición no está llegando en la ruta.
- ¿Por qué cambié el modelo y la columna nueva no aparece? El datastore guarda un retrato de los campos de la API de cuando la elegiste. Reabre Configurar datastore y vuelve a elegir la API para actualizar el retrato. Lo que cada columna ya elegida es (tipo, obligatoriedad, valor por omisión, fecha) se actualiza solo al abrir la pantalla; solo las columnas nuevas tienen que elegirse.
- ¿Por qué la paginación está lenta? Estás en Una página cada vez con muchas idas al servidor — o en Cargar todo sin filtros en una tabla enorme. Ajusta el modo al tamaño real de los datos.