Définition d’API personnalisée
Identifiant du schéma : custom-api@1
Schéma lisible par machine : custom-api.schema.json
La définition canonique d’une API personnalisée (section 6.9) : un contrat HTTP versionné qu’un environnement publie à destination de systèmes extérieurs à la plateforme, détenu par une solution et transporté dans son package. Une définition est validée de manière identique, qu’elle provienne du Centre de contrôle d’administration, d’un appel d’API, d’un assistant IA ou d’une importation de solution.
Structure
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
logicalName | string | oui | Préfixé par l’éditeur, en snake-case minuscule. Stable d’une version à l’autre. |
displayName | string | oui | Le résumé de l’opération OpenAPI. |
publisherPrefix | string | oui | Également un segment du chemin servi. |
version | integer | oui | 1–999. Servi comme segment de chemin v{version}. |
route | string | oui | Sous le segment de version, sans barre oblique initiale. Un segment entre accolades est un paramètre de chemin. |
verb | string | oui | Get, Post, Put, Patch, Delete. |
script | string | oui | Le BS Lang qu’exécute cette API. Il n’y a qu’une implémentation, et c’est celle-ci. |
requiredScope | string | oui | La portée que doit détenir l’inscription d’application de l’appelant. |
description | string | null | non | La description de l’opération OpenAPI. |
requestSchema | object | null | non | Un JSON Schema, vérifié à chaque appel. Refusé pour un verbe sans corps. Null signifie que les noms getInput du script sont le contrat (publié dans OpenAPI ; lors d’un appel de portail, une liste fermée). |
responseSchema | object | null | non | Un JSON Schema, publié dans le document OpenAPI. |
responseContentType | string | non | application/json, text/plain ou text/html — ce par quoi le contrat répond normalement, et ce que publie l’OpenAPI généré. |
isEnabled | boolean | non | Une définition désactivée est stockée et exportée, et renvoie 404. |
L’identité est une paire
(logicalName, version) identifie l’API ; (publisherPrefix, version, verb, route) est son
adresse. par_approve_invoice v1 et v2 sont un même contrat à deux âges, qui répondent
côte à côte pendant qu’un partenaire d’intégration migre — une seconde définition partageant
le nom et la version est donc un doublon, alors qu’une définition partageant le nom avec
une nouvelle version est la version suivante. Deux définitions ne peuvent jamais partager une
adresse, quel que soit leur nom.
Le chemin servi
/api/{publisherPrefix}/v{version}/{route}
Accessible aujourd’hui sous un environnement — /environments/{environmentId}/api/par/v1/invoices —
parce qu’un jeton client-credentials désigne un locataire plutôt que l’un de ses
environnements. La forme publique plus courte arrivera avec la Gateway, qui
associe un nom d’hôte à un environnement.
Ce qui s’exécute
Une seule chose : le script. Une définition nomme une route et le BS Lang qui y répond,
et il n’y a rien d’autre à choisir. Il lit la requête avec getInput, répond avec
setOutput ou setBody, choisit un statut et un type de contenu avec setStatus et
setContentType, définit des en-têtes avec setHeader, accède à d’autres tables avec les
fonctions d’enregistrement, et refuse avec throwValidationError.
Auparavant, il fallait choisir entre quatre types d’implémentation — une opération directe sur une table, un flux d’automatisation, une fonction et un message de plug-in personnalisé. Trois d’entre eux pouvaient être définis mais jamais distribués : un appel répondait donc 501 en nommant l’itération attendue, et seul le quatrième s’exécutait. C’est terminé. Un contrat dont le comportement est le propre code du créateur est plus simple à expliquer, plus simple à versionner, et c’était le seul des quatre capable d’exprimer quelque chose que la plateforme n’avait pas prévu — et il n’y a plus de 501, puisqu’il ne reste aucun type non raccordé pour en produire un.
Une logique à partager entre deux API est un script de solution, importé par son nom, et non un cinquième type.
Règles qu’un schéma ne peut pas exprimer
Vérifiées à l’enregistrement de la définition, en plus du schéma ci-dessus :
- Le
responseContentTypedéclaré est un type que la plateforme peut servir. - Une route
Read,UpdateouDeletedéclare{id}— c’est le seul endroit d’où l’id de l’enregistrement peut provenir. - Un verbe sans corps n’a pas de schéma de requête. Un verbe avec corps peut le laisser à null.
- Les schémas de requête et de réponse se compilent en JSON Schema.
- Le script s’analyse comme du BS Lang, et chaque module qu’il importe se résout en un script de solution.
Exemple
{
"logicalName": "par_create_invoice",
"displayName": "Create an invoice",
"publisherPrefix": "par",
"description": "Creates an invoice from a validated body.",
"version": 1,
"route": "invoices",
"verb": "Post",
"requestSchema": {
"type": "object",
"additionalProperties": false,
"required": ["par_name", "par_amount"],
"properties": {
"par_name": { "type": "string", "maxLength": 200 },
"par_amount": { "type": "number", "exclusiveMinimum": 0 }
}
},
"responseContentType": "application/json",
"requiredScope": "modelapps.write",
"isEnabled": true,
"script": "var id = createRecord('par_invoice', {
par_name: getInput('par_name'),
par_amount: getInput('par_amount')
});
setOutput('id', id);"
}
Le corps est vérifié selon requestSchema avant l’exécution du script : getInput lit
donc une forme déjà validée, plutôt qu’une forme contre laquelle chaque script devrait se
défendre lui-même.
Une entrée peut être un objet ou une liste
getInput remet au script ce que contient la propriété — une chaîne, un nombre, un booléen,
ou un objet ou un tableau transmis en entier, que le script parcourt par membre
(f.fileName) et avec length(). Un contrat qui décrit une entrée structurée dans son
requestSchema peut donc réellement la lire :
"par_document": {
"type": "object",
"additionalProperties": false,
"required": ["fileName", "contentType", "contentBase64"],
"properties": {
"fileName": { "type": "string", "maxLength": 260 },
"contentType": { "type": "string", "maxLength": 200 },
"sizeBytes": { "type": "integer" },
"contentBase64": { "type": "string", "maxLength": 5600000 }
}
}
Jusqu’à la dixième série de corrections des portails, un objet arrivait sous forme de texte JSON brut, que BS Lang n’a aucun moyen d’analyser — un créateur qui déclarait la forme voulue était donc moins bien loti que celui qui l’aplatissait en propriétés isolées, soit l’inverse de ce à quoi sert un schéma. L’imbrication est limitée par la limite de 64 niveaux propre à l’analyseur JSON, avant même que tout ceci n’entre en jeu.
Ce qu’elle ne fait jamais
Une API personnalisée n’élargit pas l’accès. Les fonctions d’enregistrement du script passent par la même porte que les propres écrans de la plateforme et s’exécutent en tant qu’appelant : ce sont ses autorisations sur une table, et la politique de données qui s’y applique, qui décident de ce qui est renvoyé. Un contrat soigné offre à un système extérieur une vue stable, jamais une vue plus large.