L'API GraphQL del modello
Le API di Tabella e lo schema GraphQL generato dal modello dati — operazioni CRUD, filtri, ordinamento, paginazione, totali ed enum.
Ogni app di Keplin serve un'API GraphQL su un endpoint proprio. Lo schema di quell'API non si scrive a mano: è generato da due fonti — le API che costruisci nel costruttore e il modello dati dell'app. Le tabelle del modello diventano tipi GraphQL con i loro campi e le loro relazioni; gli enum del modello diventano enum GraphQL; e un'API di Tabella trasforma una tabella in operazioni di lettura e scrittura complete, con filtri, ordinamento e paginazione, senza che tu scriva una riga di SQL.
Questa pagina copre l'endpoint, le API di Tabella e il linguaggio di interrogazione che offrono ai client.
L'endpoint dell'app
Tutte le operazioni di un'app sono servite su un unico endpoint GraphQL:
POST /api/graphql/<endereco-da-app>
Nell'app di esempio, POST /api/graphql/gestao-clientes. La richiesta è un
JSON con query e variables, come in qualsiasi servizio GraphQL — la
scheda Docs di ogni API ti dà esempi pronti da copiare. Quando l'
app è pubblicata su un indirizzo proprio, lo stesso servizio risponde anche
su /api/graphql di quell'indirizzo.
Chi può chiamare l'endpoint:
| Chi chiama | Come si autentica | Che cosa vede |
|---|---|---|
| Le schermate dell'app | Sessione dell'utente dell'app (automatico) | API pubblicate |
| Sistemi esterni | Header x-api-key — vedi Chiavi di API |
API pubblicate dentro l'ambito della chiave |
| Chi costruisce | Sessione sulla piattaforma | API pubblicate E bozze (contrassegnate come bozza) |
| Anonimi | Nulla | Soltanto API pubbliche |
Nota
Contano anche le versioni dell'app: una chiave API e le richieste anonime parlano sempre con la versione principale (o con la versione pubblicata dell' indirizzo usato); chi costruisce vede la SUA versione di lavoro. Una chiave non intercetta mai, per caso, ciò che un developer sta cambiando in quel momento.
Creare un'API di Tabella
- Crea un'API (Nuova API) con il nome
che farà da base alle operazioni — per esempio
contas. - Nella scheda Costruisci, sezione Pipeline, premi il pulsante Tabella. Il blocco Tabella occupa l'intera pipeline — non si combina con passaggi SQL, HTTP o Script.
- Scegli la tabella nel selettore Scegli la tabella… — le tabelle appaiono raggruppate per datasource, con ricerca per nome di tabella o di datasource.
- Attiva le Azioni esposte e regola i Campi inclusi (vedi più sotto).
- Salva. Per pubblicare, servono una tabella scelta e almeno un'azione attiva.


Nota
Il selettore mostra solo le tabelle importate nel modello dati. Se è vuoto, importa prima le tabelle nella scheda Modello di un datasource.
Azioni esposte
Ogni azione attiva genera un'operazione nello schema, con il nome derivato dalla
base — per l'API contas:
| Azione | Operazione generata | Che cosa fa |
|---|---|---|
| Select | getContas (query) |
Elenco con filtri/ordinamento/paginazione. Porta con sé countContas, il totale. |
| Insert | addContas (mutation) |
Crea una riga. |
| Update | updateContas (mutation) |
Aggiorna una riga per chiave primaria — parziale: cambia solo ciò che invii. |
| Delete | deleteContas (mutation) |
Elimina una riga per chiave primaria e restituisce true. |
La riga Accesso pubblico (senza sessione) controlla, azione per azione, ciò che le schermate pubbliche possono chiamare — dettagli in API pubbliche.
Campi inclusi
L'albero Campi inclusi definisce la forma della risposta: deseleziona i
campi che non vuoi esporre, ed espandi i campi di navigazione per
includere le entità correlate — ricorsivamente, come in un editor GraphQL
visuale. In un'API contas, espandere il navigator contactos fa sì che
i client possano chiedere i contatti di ogni account nella stessa chiamata.

Attenzione
Con Insert o Update attivi, i campi obbligatori della tabella (non nulli, senza valore automatico) sono sempre inclusi — senza di essi non sarebbe possibile creare righe valide. Il costruttore li mostra selezionati e bloccati.
Leggere dati: filtri, ordinamento, paginazione
La query di elenco accetta quattro argomenti: where, order, take e
skip. Un esempio completo nell'app Gestione Clienti:
query {
getContas(
where: { cidade: { eq: "Lisboa" }, estado: { neq: "ARQUIVADA" } }
order: [{ nome: ASC }]
take: 20
skip: 0
) {
id
nome
cidade
contactos {
nome
email
}
}
}
L'argomento where
Ogni campo filtrabile accetta gli operatori a seconda del tipo:
| Tipo del campo | Operatori |
|---|---|
| Testo (ed enum) | eq, neq, contains, startsWith, endsWith, gt, gte, lt, lte, in, nin |
Numeri (Int, Float) |
eq, neq, gt, gte, lt, lte, in, nin |
Boolean |
eq, neq |
ID |
eq, neq, in, nin |
E due combinatori per condizioni composte: and e or, che ricevono
elenchi di filtri.
where: {
or: [
{ cidade: { eq: "Lisboa" } }
{ cidade: { eq: "Porto" } }
]
valorAnual: { gte: 10000 }
}
Regole utili:
eq: nulltrova i record con il campo vuoto;neq: null, quelli compilati.- Intervalli di date: le date salvate come testo ISO (es.
2026-08-11) si ordinano alfabeticamente come si ordinano nel tempo, perciògt/lt/gte/ltesu testo bastano per filtrare gli intervalli —dataCriacao: { gte: "2026-01-01", lt: "2026-07-01" }. inriceve un elenco di valori;ninlo esclude.- I valori del filtro vanno sempre parametrizzati al database —
un
containscon testo malevolo non è un rischio. contains,startsWitheendsWithcercano il testo così com'è: un%o un_scritto è testo, non un carattere jolly, e maiuscole e minuscole non contano in nessun database ("ana"trova"Ana").
Ordinare e paginare
orderè un elenco di{ campo: ASC }o{ campo: DESC }— più elementi ordinano per più campi, nell'ordine dato.takelimita il numero di righe (tetto di 10 000 per richiesta) eskipsalta le prime N — insieme fanno la paginazione classica.
Il totale: count
Ogni API di Tabella con Select attivo guadagna anche count<Nome>, che
restituisce il totale delle righe dello STESSO where. La coppia naturale di una tabella
paginata è chiedere la pagina e il totale in una sola operazione, con gli alias:
query {
items: getContas(take: 10, skip: 0) { id nome }
total: countContas
}
Con un filtro, passa lo stesso where a entrambi i campi — il totale conta
esattamente le righe che l'elenco restituirebbe senza paginazione.
Scrivere dati
addContasriceve i campi inclusi come argomenti (quelli obbligatori della tabella sono obbligatori nella mutation). In alcuni database la risposta è la riga creata; in altri,true— la scheda Docs dell' API mostra la forma esatta nel tuo caso. Una colonna non nulla con un valore predefinito nel database, o generata dal database, è facoltativa: se non la invii, resta il valore del database.updateContasriceve la chiave primaria (obbligatoria) e gli altri campi come opzionali — aggiorna solo ciò che invii — e restituisce la riga aggiornata.deleteContasriceve la chiave primaria e restituiscetrue.
mutation ($nome: String!, $cidade: String) {
addContas(nome: $nome, cidade: $cidade) {
id
nome
}
}
Nota
I permessi dell'app si applicano qui, sempre: se l'utente dell'app
può vedere solo gli account della sua squadra, getContas e countContas
restituiscono — e contano — solo quelli, chiunque li chiami (schermata, report
o workflow).
Una colonna di data e ora senza fuso (timestamp, datetime) esce dall'API
così com'è nel database, in ISO senza fuso (2026-09-25T09:30:00): è l'ora
scritta lì, e arriva uguale a chi la legge in qualsiasi fuso. Una colonna con
il solo giorno esce come il giorno (2026-09-25). Una colonna con fuso
(timestamptz, datetimeoffset, il timestamp di MySQL) esce come istante,
in ISO con fuso (2026-09-25T09:30:00.000Z), e le schermate la mostrano nel
fuso di chi la legge. Un Date restituito da un passo SQL di una API pipeline
esce anch'esso in ISO con fuso. Le schermate le mostrano tutte nella lingua
dell'app. Un valore scritto con il tipo sbagliato è accettato quando non
lascia dubbi: «12» in un Int, 1000 in una String, «true» in un
Boolean.
Altre tre regole delle scritture:
- La chiave primaria può andare anche nell'
add. Con una chiave generata dal database resta fuori; con una chiave naturale (un codice articolo, un codice paese) è così che si crea il record. - Un
updatesu un record che non esiste, o fuori dall'ambito di chi lo chiede, non cambia nulla e restituiscenull; undeletenelle stesse condizioni restituiscefalse. - Un rifiuto del database (chiave ripetuta, record con dipendenti, campo obbligatorio vuoto, valore troppo lungo) arriva come messaggio chiaro, con il campo quando il database lo indica.
Una richiesta può annidare fino a 12 livelli di relazioni e usare fino a 100 alias. Le relazioni si leggono a lotti: le righe chieste nello stesso momento vanno in un'unica query.
Enum
Un enum espone un insieme fisso di valori nello schema GraphQL — lo stato di un account, la fase di un'opportunità. Si gestiscono nella pagina API, scheda Enum:
- Premi Nuovo enum.
- Dai un nome (es.
EstadoConta) e, se aiuta, una descrizione. - Aggiungi valori con Aggiungi valore — ogni valore ha un identificatore (value), un'etichetta e un colore opzionali. Il colore e l'etichetta sono usati dalle schermate; il value è ciò che viaggia nell'API.
- Salva.


Un enum si usa in due posti: come tipo di un campo del modello (il campo comincia ad accettare soltanto quei valori, e nei filtri si comporta come testo) e come tipo di argomento di un'API. Nello schema, i client vedono l' enum con i suoi valori — il completamento automatico dell'ambiente di test li suggerisce.
Attenzione
Eliminare un enum è permanente e i campi/argomenti che lo usavano smettono di referenziarlo. Preferisci modificare i valori piuttosto che eliminare l'enum.
Quando scrivi un valore di enum, l'API accetta il nome mostrato dallo schema,
senza virgolette (estado: Em_curso), e anche il valore così come è salvato,
tra virgolette (estado: "Em curso"). È così che lo inviano i moduli e
keplin.api.mutate. Un valore che non appartiene all'enum continua a essere
rifiutato.
Esplorare lo schema in GraphiQL
La scheda Prova di qualsiasi API include GraphiQL — l'ambiente interattivo dell'endpoint dell'app. Scrivi l'operazione a sinistra, esegui, e vedi la risposta a destra; il completamento automatico conosce l'intero schema, operazioni di Tabella comprese. Il pulsante Apri in finestra ti dà lo stesso ambiente a schermo intero.

Siccome sei autenticato sulla piattaforma, GraphiQL esegue come un client ma vede anche le bozze — ogni operazione in bozza appare con la descrizione "RASCUNHO" nella documentazione dello schema. E risponde sulla tua versione di lavoro: ciò che stai disegnando è ciò che stai provando.
Perché no…?
- Perché non vedo l'operazione
getContasdall'esterno? O l'API è in bozza (pubblicala), o l'azione Select non è attiva, oppure la tua chiave non ha quell'endpoint nell'ambito. - Perché non appare un campo nella risposta? Non è selezionato in Campi inclusi — i client possono selezionare solo ciò che l'API include.
- Perché
addContasrestituisce untrueinvece della riga? Dipende dal database dietro la tabella. Quando ti serve sempre la riga, fai seguire ungetContasfiltrato per chiave. - Perché lo schema è cambiato senza che io toccassi le API? Lo schema è generato dal modello: importare colonne nuove, cambiare un enum o disattivare un' entità si riflette nell'API alla chiamata successiva.