KEPLIN Docs

L'API GraphQL du modèle

Les APIs de Table et le schéma GraphQL généré à partir du modèle de données — opérations CRUD, filtres, tri, pagination, totaux et enums.

Chaque app de Keplin sert une API GraphQL sur un endpoint qui lui est propre. Le schéma de cette API ne s'écrit pas à la main : il est généré à partir de deux sources — les APIs que vous construisez dans le constructeur et le modèle de données de l'app. Les tables du modèle deviennent des types GraphQL avec leurs champs et leurs relations ; les enums du modèle deviennent des enums GraphQL ; et une API de Table transforme une table en opérations de lecture et d'écriture complètes, avec filtres, tri et pagination, sans que vous écriviez une ligne de SQL.

Cette page couvre l'endpoint, les APIs de Table et le langage de requête qu'elles offrent aux clients.

L'endpoint de l'app

Toutes les opérations d'une app sont servies sur un unique endpoint GraphQL :

POST /api/graphql/<adresse-de-l-app>

Dans l'app d'exemple, POST /api/graphql/gestao-clientes. La requête est un JSON avec query et variables, comme sur n'importe quel service GraphQL — l'onglet Docs de chaque API vous donne des exemples prêts à copier. Quand l'app est publiée à une adresse propre, le même service répond aussi sur /api/graphql de cette adresse.

Qui peut appeler l'endpoint :

Qui appelle Comment il s'authentifie Ce qu'il voit
Les écrans de l'app Session de l'utilisateur de l'app (automatique) Les APIs publiées
Les systèmes externes Header x-api-key — voir Clés d'API Les APIs publiées dans le scope de la clé
Celui qui construit Session sur la plateforme Les APIs publiées ET les brouillons (marqués comme brouillon)
Les anonymes Rien Uniquement les APIs publiques

Note

Les versions de l'app comptent aussi : une clé API et les requêtes anonymes parlent toujours à la version principale (ou à la version publiée de l'adresse utilisée) ; celui qui construit voit SA version de travail. Une clé n'attrape jamais, par hasard, ce qu'un développeur est en train de changer.

Créer une API de Table

  1. Créez une API (Nouvelle API) avec le nom qui servira de base aux opérations — par exemple contas.
  2. Dans l'onglet Construire, section Pipeline, cliquez sur le bouton Table. Le bloc Table occupe tout le pipeline — il ne se combine pas avec des étapes SQL, HTTP ou Script.
  3. Choisissez la table dans le sélecteur Choisissez la table… — les tables apparaissent groupées par datasource, avec une recherche par nom de table ou de datasource.
  4. Activez les Actions exposées et ajustez les Champs inclus (voir plus bas).
  5. Enregistrer. Pour publier, il faut une table choisie et au moins une action active.

Le bloc Table dans le constructeur, avec la table choisie et les actions exposées.
Le bloc Table dans le constructeur, avec la table choisie et les actions exposées.

Le sélecteur de table : datasource → table, avec recherche.
Le sélecteur de table : datasource → table, avec recherche.

Note

Le sélecteur ne montre que les tables importées dans le modèle de données. S'il est vide, importez d'abord des tables dans l'onglet Modèle d'un datasource.

Actions exposées

Chaque action activée génère une opération dans le schéma, avec le nom dérivé de la base — pour l'API contas :

Action Opération générée Ce qu'elle fait
Select getContas (query) Liste avec filtres/tri/pagination. Elle apporte avec elle countContas, le total.
Insert addContas (mutation) Crée une ligne.
Update updateContas (mutation) Met à jour une ligne par sa clé primaire — partiel : ne change que ce que vous envoyez.
Delete deleteContas (mutation) Supprime une ligne par sa clé primaire et renvoie true.

La ligne Accès public (sans session) contrôle, action par action, ce que les écrans publics peuvent appeler — détails dans APIs publiques.

Champs inclus

L'arborescence Champs inclus définit la forme de la réponse : décochez les champs que vous ne voulez pas exposer, et développez les champs de navigation pour inclure les entités liées — récursivement, comme dans un éditeur GraphQL visuel. Dans une API contas, développer le navigator contactos permet aux clients de demander les contacts de chaque compte dans le même appel.

L'arborescence de champs d'une API de Table, avec un champ de navigation développé.
L'arborescence de champs d'une API de Table, avec un champ de navigation développé.

Attention

Avec Insert ou Update actifs, les champs obligatoires de la table (non nuls, sans valeur automatique) sont toujours inclus — sans eux, il ne serait pas possible de créer des lignes valides. Le constructeur les montre cochés et verrouillés.

Lire des données : filtres, tri, pagination

La query de liste accepte quatre arguments : where, order, take et skip. Un exemple complet dans l'app Gestion des Clients :

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

L'argument where

Chaque champ filtrable accepte des opérateurs selon le type :

Type du champ Opérateurs
Texte (et enums) eq, neq, contains, startsWith, endsWith, gt, gte, lt, lte, in, nin
Nombres (Int, Float) eq, neq, gt, gte, lt, lte, in, nin
Boolean eq, neq
ID eq, neq, in, nin

Et deux combinateurs pour des conditions composées : and et or, qui reçoivent des listes de filtres.

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

Des règles utiles :

  • eq: null trouve les enregistrements dont le champ est vide ; neq: null, ceux qui sont remplis.
  • Intervalles de dates : les dates conservées en texte ISO (ex. 2026-08-11) se trient alphabétiquement comme elles se trient dans le temps, donc gt/lt/gte/lte en texte suffisent pour filtrer des intervalles — dataCriacao: { gte: "2026-01-01", lt: "2026-07-01" }.
  • in reçoit une liste de valeurs ; nin l'exclut.
  • Les valeurs du filtre partent toujours paramétrées vers la base de données — un contains avec du texte malveillant n'est pas un risque.
  • contains, startsWith et endsWith cherchent le texte tel quel : un % ou un _ écrit est du texte, pas un joker, et la casse ne compte dans aucune base de données ("ana" trouve "Ana").

Trier et paginer

  • order est une liste de { champ: ASC } ou { champ: DESC } — plusieurs éléments trient par plusieurs champs, dans l'ordre donné.
  • take limite le nombre de lignes (plafond de 10 000 par requête) et skip saute les N premières — ensemble, ils font la pagination classique.

Le total : count

Chaque API de Table avec Select actif gagne aussi count<Nom>, qui renvoie le total de lignes du MÊME where. Le duo naturel d'une table paginée est de demander la page et le total en une seule opération, avec des alias :

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

Avec un filtre, passez le même where aux deux champs — le total compte exactement les lignes que la liste renverrait sans pagination.

Écrire des données

  • addContas reçoit les champs inclus comme arguments (ceux qui sont obligatoires dans la table le sont dans la mutation). Sur certaines bases de données, la réponse est la ligne créée ; sur d'autres, true — l'onglet Docs de l'API montre la forme exacte dans votre cas. Une colonne non nulle avec une valeur par défaut dans la base de données, ou générée par elle, est facultative : si vous ne l'envoyez pas, la valeur de la base s'applique.
  • updateContas reçoit la clé primaire (obligatoire) et les autres champs comme facultatifs — il ne met à jour que ce que vous envoyez — et renvoie la ligne mise à jour.
  • deleteContas reçoit la clé primaire et renvoie true.
mutation ($nome: String!, $cidade: String) {
  addContas(nome: $nome, cidade: $cidade) {
    id
    nome
  }
}

Note

Les permissions de l'app s'appliquent ici, toujours : si l'utilisateur de l'app ne peut voir que les comptes de son équipe, getContas et countContas ne renvoient — et ne comptent — que ceux-là, quel que soit l'appelant (écran, rapport ou workflow).

Une colonne de date et heure sans fuseau (timestamp, datetime) sort de l'API telle qu'elle est dans la base de données, en ISO sans fuseau (2026-09-25T09:30:00) : c'est l'heure qui y est écrite, et elle arrive identique à qui la lit dans n'importe quel fuseau. Une colonne ne contenant que le jour sort comme le jour (2026-09-25). Une colonne avec fuseau (timestamptz, datetimeoffset, le timestamp de MySQL) sort comme un instant, en ISO avec fuseau (2026-09-25T09:30:00.000Z), et les écrans l'affichent dans le fuseau de la personne qui la lit. Un Date renvoyé par une étape SQL d'une API de pipeline sort aussi en ISO avec fuseau. Les écrans les affichent toutes dans la langue de l'app. Une valeur écrite avec le mauvais type est acceptée quand il n'y a pas de doute : « 12 » dans un Int, 1000 dans un String, « true » dans un Boolean.

Trois règles de plus pour les écritures :

  • La clé primaire peut aussi aller dans le add. Avec une clé générée par la base, elle reste de côté ; avec une clé naturelle (un code d'article, un code de pays), c'est ainsi que l'enregistrement se crée.
  • Un update sur un enregistrement qui n'existe pas, ou hors du périmètre de l'appelant, ne change rien et renvoie null ; un delete dans les mêmes conditions renvoie false.
  • Un refus de la base (clé répétée, enregistrement avec des dépendants, champ obligatoire vide, valeur trop longue) arrive sous forme de message clair, avec le champ quand la base l'indique.

Une requête peut imbriquer jusqu'à 12 niveaux de relations et utiliser jusqu'à 100 alias. Les relations se lisent par lots : les lignes demandées en même temps passent en une seule requête.

Enums

Un enum expose un ensemble fixe de valeurs dans le schéma GraphQL — l'état d'un compte, la phase d'une opportunité. Ils se gèrent sur la page APIs, onglet Enums :

  1. Cliquez sur Nouvel enum.
  2. Donnez un nom (ex. EstadoConta) et, si cela aide, une description.
  3. Ajoutez des valeurs avec Ajouter une valeur — chaque valeur a un identifiant (value), un libellé et une couleur facultatifs. La couleur et le libellé sont utilisés par les écrans ; le value est ce qui voyage dans l'API.
  4. Enregistrer.

La page APIs, onglet Enums — l'app Gestion des Clients n'a pas encore d'enums créés.
La page APIs, onglet Enums — l'app Gestion des Clients n'a pas encore d'enums créés.

Créer un enum : des valeurs avec identifiant, libellé et couleur.
Créer un enum : des valeurs avec identifiant, libellé et couleur.

Un enum s'utilise à deux endroits : comme type d'un champ du modèle (le champ n'accepte plus que ces valeurs, et dans les filtres il se comporte comme du texte) et comme type d'argument d'une API. Dans le schéma, les clients voient l'enum avec ses valeurs — l'autocomplétion de l'environnement de test les suggère.

Attention

Supprimer un enum est définitif et les champs/arguments qui l'utilisaient cessent de le référencer. Préférez modifier les valeurs plutôt que supprimer l'enum.

Pour écrire une valeur d'enum, l'API accepte le nom affiché par le schéma, sans guillemets (estado: Em_curso), et aussi la valeur telle qu'elle est enregistrée, entre guillemets (estado: "Em curso"). C'est ainsi que les formulaires et keplin.api.mutate l'envoient. Une valeur qui n'appartient pas à l'enum reste refusée.

Explorer le schéma dans GraphiQL

L'onglet Tester de n'importe quelle API inclut GraphiQL — l'environnement interactif de l'endpoint de l'app. Vous écrivez l'opération à gauche, vous exécutez, et vous voyez la réponse à droite ; l'autocomplétion connaît tout le schéma, opérations de Table incluses. Le bouton Ouvrir dans une fenêtre vous donne le même environnement en plein écran.

GraphiQL dans l'onglet Tester, avec la query de liste prête à être exécutée.
GraphiQL dans l'onglet Tester, avec la query de liste prête à être exécutée.

Comme vous êtes authentifié sur la plateforme, GraphiQL s'exécute comme un client mais voit aussi les brouillons — chaque opération en brouillon apparaît avec la description « RASCUNHO » dans la documentation du schéma. Et il répond au sujet de votre version de travail : ce que vous dessinez est ce que vous testez.

Pourquoi ne… ?

  • Pourquoi je ne vois pas l'opération getContas de l'extérieur ? Soit l'API est en brouillon (publiez-la), soit l'action Select n'est pas active, soit votre clé n'a pas cet endpoint dans son scope.
  • Pourquoi un champ n'apparaît-il pas dans la réponse ? Il n'est pas coché dans Champs inclus — les clients ne peuvent sélectionner que ce que l'API inclut.
  • Pourquoi addContas renvoie-t-il un true au lieu de la ligne ? Cela dépend de la base de données derrière la table. Quand vous avez toujours besoin de la ligne, enchaînez avec un getContas filtré par la clé.
  • Pourquoi le schéma a-t-il changé sans que je touche aux APIs ? Le schéma est généré à partir du modèle : importer de nouvelles colonnes, changer un enum ou désactiver une entité se reflète dans l'API dès l'appel suivant.