KEPLIN Docs

La API GraphQL del modelo

Las APIs de Tabla y el schema GraphQL generado del modelo de datos — operaciones CRUD, filtros, ordenación, paginación, totales y enums.

Cada app de Keplin sirve una API GraphQL en un endpoint propio. El schema de esa API no se escribe a mano: se genera a partir de dos fuentes — las APIs que construyes en el constructor y el modelo de datos de la app. Las tablas del modelo se convierten en tipos GraphQL con sus campos y relaciones; los enums del modelo se convierten en enums GraphQL; y una API de Tabla transforma una tabla en operaciones de lectura y escritura completas, con filtros, ordenación y paginación, sin que escribas una línea de SQL.

Esta página cubre el endpoint, las APIs de Tabla y el lenguaje de consulta que ofrecen a los clientes.

El endpoint de la app

Todas las operaciones de una app se sirven en un único endpoint GraphQL:

POST /api/graphql/<direccion-de-la-app>

En la app de ejemplo, POST /api/graphql/gestao-clientes. La petición es un JSON con query y variables, como en cualquier servicio GraphQL — la pestaña Docs de cada API te da ejemplos listos para copiar. Cuando la app está publicada en una dirección propia, el mismo servicio responde también en /api/graphql de esa dirección.

Quién puede llamar al endpoint:

Quién llama Cómo se autentica Qué ve
Las pantallas de la app Sesión del usuario de la app (automático) APIs publicadas
Sistemas externos Header x-api-key — ver Claves de API APIs publicadas dentro del scope de la clave
Quien construye Sesión en la plataforma APIs publicadas Y borradores (marcados como borrador)
Anónimos Nada Solo APIs públicas

Nota

Las versiones de la app también cuentan: una clave API y las peticiones anónimas hablan siempre con la versión principal (o con la versión publicada de la dirección usada); quien construye ve SU versión de trabajo. Una clave nunca atrapa, por casualidad, lo que un developer está a medio cambiar.

Crear una API de Tabla

  1. Crea una API (Nueva API) con el nombre que va a servir de base a las operaciones — por ejemplo contas.
  2. En la pestaña Construir, sección Pipeline, pulsa el botón Tabla. El bloque Tabla ocupa el pipeline entero — no se combina con pasos SQL, HTTP o Script.
  3. Elige la tabla en el selector Elige la tabla… — las tablas aparecen agrupadas por datasource, con búsqueda por nombre de tabla o de datasource.
  4. Activa las Acciones expuestas y ajusta los Campos incluidos (ver abajo).
  5. Guardar. Para publicar, necesita tabla elegida y al menos una acción activa.

El bloque Tabla en el constructor, con la tabla elegida y las acciones expuestas.
El bloque Tabla en el constructor, con la tabla elegida y las acciones expuestas.

El selector de tabla: datasource → tabla, con búsqueda.
El selector de tabla: datasource → tabla, con búsqueda.

Nota

El selector solo muestra tablas importadas al modelo de datos. Si está vacío, importa tablas en la pestaña Modelo de un datasource primero.

Acciones expuestas

Cada acción activa genera una operación en el schema, con el nombre derivado de la base — para la API contas:

Acción Operación generada Qué hace
Select getContas (query) Lista con filtros/ordenación/paginación. Trae consigo countContas, el total.
Insert addContas (mutation) Crea una fila.
Update updateContas (mutation) Actualiza una fila por la clave primaria — parcial: solo cambia lo que envíes.
Delete deleteContas (mutation) Elimina una fila por la clave primaria y devuelve true.

La fila Acceso público (sin sesión) controla, acción a acción, lo que las pantallas públicas pueden llamar — detalles en APIs públicas.

Campos incluidos

El árbol Campos incluidos define la forma de la respuesta: desmarca los campos que no quieres exponer, y expande los campos de navegación para incluir entidades relacionadas — recursivamente, como en un editor GraphQL visual. En una API contas, expandir el navegador contactos hace que los clientes puedan pedir los contactos de cada cuenta en la misma llamada.

El árbol de campos de una API de Tabla, con un campo de navegación expandido.
El árbol de campos de una API de Tabla, con un campo de navegación expandido.

Atención

Con Insert o Update activos, los campos obligatorios de la tabla (no nulos, sin valor automático) quedan siempre incluidos — sin ellos no sería posible crear filas válidas. El constructor los muestra marcados y bloqueados.

Leer datos: filtros, ordenación, paginación

La query de lista acepta cuatro argumentos: where, order, take y skip. Un ejemplo completo en la app Gestión de Clientes:

query {
  getContas(
    where: { cidade: { eq: "Lisboa" }, estado: { neq: "ARQUIVADA" } }
    order: [{ nome: ASC }]
    take: 20
    skip: 0
  ) {
    id
    nome
    cidade
    contactos {
      nome
      email
    }
  }
}

El argumento where

Cada campo filtrable acepta operadores según el tipo:

Tipo del campo Operadores
Texto (y enums) eq, neq, contains, startsWith, endsWith, gt, gte, lt, lte, in, nin
Números (Int, Float) eq, neq, gt, gte, lt, lte, in, nin
Boolean eq, neq
ID eq, neq, in, nin

Y dos combinadores para condiciones compuestas: and y or, que reciben listas de filtros.

where: {
  or: [
    { cidade: { eq: "Lisboa" } }
    { cidade: { eq: "Porto" } }
  ]
  valorAnual: { gte: 10000 }
}

Reglas útiles:

  • eq: null encuentra los registros con el campo vacío; neq: null, los rellenos.
  • Intervalos de fechas: las fechas guardadas como texto ISO (ej. 2026-08-11) ordenan alfabéticamente como ordenan en el tiempo, por eso gt/lt/gte/lte en texto bastan para filtrar intervalos — dataCriacao: { gte: "2026-01-01", lt: "2026-07-01" }.
  • in recibe una lista de valores; nin la excluye.
  • Los valores del filtro van siempre parametrizados a la base de datos — un contains con texto malicioso no es un riesgo.
  • contains, startsWith y endsWith buscan el texto tal cual: un % o un _ escrito es texto, no un comodín, y las mayúsculas y minúsculas no cuentan en ninguna base de datos ("ana" encuentra "Ana").

Ordenar y paginar

  • order es una lista de { campo: ASC } o { campo: DESC } — varios elementos ordenan por varios campos, en el orden dado.
  • take limita el número de filas (techo de 10 000 por petición) y skip salta las primeras N — juntos hacen la paginación clásica.

El total: count

Cada API de Tabla con Select activo gana también count<Nombre>, que devuelve el total de filas del MISMO where. El par natural de una tabla paginada es pedir la página y el total en una sola operación, con aliases:

query {
  items: getContas(take: 10, skip: 0) { id nome }
  total: countContas
}

Con filtro, pasa el mismo where a los dos campos — el total cuenta exactamente las filas que la lista devolvería sin paginación.

Escribir datos

  • addContas recibe los campos incluidos como argumentos (los obligatorios de la tabla son obligatorios en la mutation). En algunas bases de datos la respuesta es la fila creada; en otras, true — la pestaña Docs de la API muestra la forma exacta en tu caso. Una columna no nula con valor por omisión en la base de datos, o generada por ella, es opcional: si no la envías, queda el valor de la base.
  • updateContas recibe la clave primaria (obligatoria) y los demás campos como opcionales — solo actualiza lo que envíes — y devuelve la fila actualizada.
  • deleteContas recibe la clave primaria y devuelve true.
mutation ($nome: String!, $cidade: String) {
  addContas(nome: $nome, cidade: $cidade) {
    id
    nome
  }
}

Nota

Los permisos de la app se aplican aquí, siempre: si el usuario de la app solo puede ver las cuentas de su equipo, getContas y countContas devuelven — y cuentan — solo esas, llame quien llame (pantalla, informe o workflow).

Una columna de fecha y hora sin zona horaria (timestamp, datetime) sale de la API tal como está en la base de datos, en ISO sin zona (2026-09-25T09:30:00): es la hora que está escrita allí, y llega igual a quien la lee en cualquier zona horaria. Una columna solo de día sale como el día (2026-09-25). Una columna con zona horaria (timestamptz, datetimeoffset, el timestamp de MySQL) sale como instante, en ISO con zona (2026-09-25T09:30:00.000Z), y las pantallas la muestran en la zona horaria de quien la lee. Un Date devuelto por un paso SQL de una API de pipeline sale también en ISO con zona. Las pantallas las muestran todas en el idioma de la app. Un valor escrito con el tipo equivocado se acepta cuando no deja dudas: «12» en un Int, 1000 en un String, «true» en un Boolean.

Tres reglas más de las escrituras:

  • La clave primaria también puede ir en el add. Con una clave generada por la base queda fuera; con una clave natural (un código de artículo, un código de país) es así como se crea el registro.
  • Un update a un registro que no existe, o que está fuera del ámbito de quien lo pide, no cambia nada y devuelve null; un delete en las mismas condiciones devuelve false.
  • Un rechazo de la base (clave repetida, registro con dependientes, campo obligatorio vacío, valor demasiado largo) llega como un mensaje claro, con el campo cuando la base lo indica.

Una petición puede encadenar hasta 12 niveles de relaciones y usar hasta 100 alias. Las relaciones se leen por lotes: las filas pedidas a la vez van en una sola consulta.

Enums

Un enum expone un conjunto fijo de valores en el schema GraphQL — el estado de una cuenta, la fase de una oportunidad. Se gestionan en la página APIs, pestaña Enums:

  1. Pulsa Nuevo enum.
  2. Dale un nombre (ej. EstadoConta) y, si ayuda, una descripción.
  3. Añade valores con Añadir valor — cada valor tiene un identificador (value), un label y un color opcionales. El color y el label los usan las pantallas; el value es lo que viaja en la API.
  4. Guardar.

La página APIs, pestaña Enums — la app Gestión de Clientes aún no tiene enums creados.
La página APIs, pestaña Enums — la app Gestión de Clientes aún no tiene enums creados.

Crear un enum: valores con identificador, label y color.
Crear un enum: valores con identificador, label y color.

Un enum se usa en dos sitios: como tipo de un campo del modelo (el campo pasa a aceptar solo esos valores, y en los filtros se comporta como texto) y como tipo de argumento de una API. En el schema, los clientes ven el enum con sus valores — el autocompletado del entorno de prueba los sugiere.

Atención

Eliminar un enum es permanente y los campos/argumentos que lo usaban dejan de referenciarlo. Prefiere editar los valores antes que eliminar el enum.

Al escribir un valor de enum, la API acepta el nombre que muestra el schema, sin comillas (estado: Em_curso), y también el valor tal como está guardado, entre comillas (estado: "Em curso"). Así lo envían los formularios y keplin.api.mutate. Un valor que no pertenece al enum sigue siendo rechazado.

Explorar el schema en GraphiQL

La pestaña Probar de cualquier API incluye el GraphiQL — el entorno interactivo del endpoint de la app. Escribes la operación a la izquierda, ejecutas, y ves la respuesta a la derecha; el autocompletado conoce el schema entero, operaciones de Tabla incluidas. El botón Abrir en ventana te da el mismo entorno a pantalla completa.

El GraphiQL en la pestaña Probar, con la query de lista lista para ejecutar.
El GraphiQL en la pestaña Probar, con la query de lista lista para ejecutar.

Como estás autenticado en la plataforma, el GraphiQL ejecuta como un cliente pero ve también los borradores — cada operación en borrador aparece con la descripción «BORRADOR» en la documentación del schema. Y responde sobre tu versión de trabajo: lo que estás diseñando es lo que estás probando.

¿Por qué no…?

  • ¿Por qué no veo la operación getContas desde fuera? O la API está en borrador (publícala), o la acción Select no está activa, o tu clave no tiene ese endpoint en el scope.
  • ¿Por qué no aparece un campo en la respuesta? No está marcado en Campos incluidos — los clientes solo pueden seleccionar lo que la API incluya.
  • ¿Por qué addContas devuelve un true en vez de la fila? Depende de la base de datos detrás de la tabla. Cuando necesites siempre la fila, sigue con un getContas filtrado por la clave.
  • ¿Por qué cambió el schema sin que yo tocara las APIs? El schema se genera del modelo: importar columnas nuevas, cambiar un enum o desactivar una entidad se refleja en la API en la llamada siguiente.