Praxsuite

Query

Vincent Depassier · 17 de septiembre de 2026

POST /{workspaceId}/query

El único endpoint de datos. Las lecturas y las escrituras pasan las dos por aquí, y cuál te toca lo decide el cuerpo, no la ruta ni el método.

Auth: API key (sk_live_ o pk_live_), o un JWT de usuario final.

Pedido

POST https://gateway.praxsuite.com/{workspaceId}/query
Authorization: Bearer sk_live_...
Content-Type: application/json
{
  "refs": { "Clientes": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" },
  "query": {
    "from": "Clientes",
    "select": ["Nombre", "Email"],
    "where": [{ "field": "Estado", "op": "eq", "value": "Activo" }],
    "orderBy": [{ "field": "Nombre", "dir": "asc" }],
    "limit": 50
  },
  "includeTotalCount": false
}

Campo

Tipo

Notas

refs

objeto

Tus alias → GUIDs de tabla. Obligatorio, de 1 a 20 entradas

query

objeto

Una lectura. Mutuamente excluyente con mutation

mutation

objeto

Una escritura. Mutuamente excluyente con query

includeTotalCount

booleano

Llena meta.total. Cuesta una segunda pasada — pídelo sólo si dibujas un total de páginas

refs es además la lista completa de lo que el pedido puede tocar, y eso es lo que le permite al gateway contrastarlo con tus permisos antes de hacer ningún trabajo.

query

Campo

Tipo

Notas

from

string

Un alias de refs

select

array

Nombres de columna, objetos de relación u objetos de agregación. Omítelo para todas las columnas legibles

where

array

Condiciones, unidas con AND en el mismo nivel

orderBy

array

`{ "field": …, "dir": "asc" \

"desc" }`

groupBy

array

Nombres de columna, para consultas agregadas

having

array

Condiciones aplicadas después de agrupar

limit

número

Por defecto 50, techo 1000, y un permiso de tabla puede bajarlo

offset

número

Filas a saltear

Un item de agregación es { "field": "Total", "fn": "sum", "alias": "Facturado" }.

select: "*" no es un error cuando tus permisos de columna son angostos: devuelve las columnas que puedes leer, y nada te avisa de que las otras existen. Nombrar una tabla para la que no tienes permiso sí es un error: 403 SCOPE_VIOLATION.

mutation

Campo

Tipo

Notas

type

string

insert, update o delete

table

string

Un alias de refs

values

array

Para insert: objetos de fila, columna → valor

set

objeto

Para update: columna → nuevo valor

where

array

Obligatorio en update y delete. No existe la mutación sin alcance

returning

bool o array

Para insert: true para todas las columnas legibles, o una lista de nombres. Omitido no devuelve nada

notify

objeto

Anunciar la escritura en un bus del Event Bus una vez confirmada

notify no lleva payload propio: { "bus": "{topic}:{instance}" }. El cuerpo del evento se arma con lo que devolvió la base, nunca con lo que mandó el pedido, así que una escritura no sirve para retransmitir contenido arbitrario. El topic tiene que estar declarado, habilitado y publicable por quien llama — todo se verifica antes de ejecutar la mutación, así que una escritura nunca se confirma y después falla al anunciar. Si la verificación falla, recibes 403 NOTIFY_DENIED y no se escribe nada.

Cómo se verifica un pedido

Tus permisos aplican en las dos direcciones, tanto en lecturas como en escrituras. Nada de lo que mande tu cliente puede quitar ninguna de las dos — por eso un delete nunca alcanza filas que la credencial no hubiera podido leer.

Un pedido se autoriza antes de ejecutar nada, así que una tabla fuera de tus permisos es 403 y no un resultado vacío; después el filtro de filas del permiso se suma con AND a todo where; y la respuesta se vuelve a filtrar a la salida, sacando las columnas ilegibles y aplicando las reglas de enmascarado. Una columna sobre la que puedes filtrar pero no leer igual acota las filas.

Respuesta — lectura

{
  "data": [
    { "Nombre": "Acme Corp", "Email": "hola@acme.com" }
  ],
  "meta": {
    "count": 1,
    "limit": 50,
    "offset": 0,
    "total": null,
    "durationMs": 4
  }
}

meta.total es null salvo que hayas pedido includeTotalCount. Lee meta.limit en vez de asumir que respetaron el tuyo.

Respuesta — escritura

{
  "affectedRows": 1,
  "data": [{ "Id": "…", "Nombre": "Acme Corp" }],
  "meta": { "type": "insert", "table": "Clientes", "durationMs": 7 }
}

data aparece sólo en un insert que pidió returning.

Cabeceras de respuesta

Cabecera

Cuándo

X-Cache, X-Cache-Credential, ETag, Cache-Control

La credencial tiene caché habilitada

X-Api-Usage-Overage, X-Api-Usage-Current, X-Api-Usage-Included

El workspace pasó sus llamadas incluidas, con pago por uso

Con la caché activa, devuelve un ETag anterior como If-None-Match para recibir 304 Not Modified en vez del cuerpo.

Errores

El sobre estructurado, con code. La tabla completa está en Errores y límites — los específicos de este endpoint son INVALID_REQUEST, INVALID_REFS, INVALID_QUERY, INVALID_MUTATION, SCOPE_VIOLATION, NOTIFY_DENIED, QUERY_TIMEOUT y MUTATION_TIMEOUT.

Toda llamada —incluidas las que fallan— se escribe en el log de consultas, con el cuerpo, quién llamó, la IP de origen y la duración. API Gateway → Logs.

Más a fondo

La gramática en sí —operadores, relaciones, agregaciones, semántica de mutaciones, el modelo de seguridad— está documentada completa en API Gateway → PraxQL, y se puede probar sin escribir código en el Playground.