Naar hoofdinhoud gaan

Definitie van een aangepaste API

Schema-id: custom-api@1 Machineleesbaar schema: custom-api.schema.json

De canonieke definitie van een aangepaste API (sectie 6.9): een geversioneerd HTTP-contract dat een omgeving publiceert voor systemen buiten het platform, eigendom van een oplossing en meereizend in het pakket daarvan. Een definitie wordt op dezelfde manier gevalideerd, of ze nu uit het Beheercentrum komt, uit een API-aanroep, van een AI-assistent of uit een oplossingsimport.

Structuur​

VeldTypeVerplichtOpmerkingen
logicalNametekenreeksjaMet uitgeversvoorvoegsel, in kleine letters snake_case. Stabiel over versies heen.
displayNametekenreeksjaDe samenvatting van de OpenAPI-operatie.
publisherPrefixtekenreeksjaOok een segment van het geserveerde pad.
versiongeheel getalja1–999. Geserveerd als padsegment v{version}.
routetekenreeksjaOnder het versiesegment, zonder voorloopslash. Een segment tussen accolades is een padparameter.
verbtekenreeksjaGet, Post, Put, Patch, Delete.
scripttekenreeksjaDe BS Lang die deze API uitvoert. Er is één implementatie, en dit is hem.
requiredScopetekenreeksjaDe scope die de app-registratie van een aanroeper moet hebben.
descriptiontekenreeks | nullneeDe beschrijving van de OpenAPI-operatie.
requestSchemaobject | nullneeEen JSON Schema, bij elke aanroep gecontroleerd. Geweigerd voor een werkwoord zonder body. Null betekent: de getInput-namen van het script zijn het contract (in OpenAPI gepubliceerd; bij een portaalaanroep een gesloten lijst).
responseSchemaobject | nullneeEen JSON Schema, gepubliceerd in het OpenAPI-document.
responseContentTypetekenreeksneeapplication/json, text/plain of text/html — waarmee het contract normaal antwoordt, en wat de gegenereerde OpenAPI publiceert.
isEnabledbooleaansneeEen uitgeschakelde definitie wordt opgeslagen en geëxporteerd, en retourneert 404.

Identiteit is een paar​

(logicalName, version) identificeert de API; (publisherPrefix, version, verb, route) is het adres ervan. par_approve_invoice v1 en v2 zijn één contract op twee leeftijden en antwoorden naast elkaar terwijl een integratiepartner migreert — dus een tweede definitie met dezelfde naam en dezelfde versie is een duplicaat, terwijl een definitie met dezelfde naam en een nieuwe versie de volgende versie is. Twee definities mogen nooit hetzelfde adres delen, hoe ze ook heten.

Het geserveerde pad​

/api/{publisherPrefix}/v{version}/{route}

Vandaag bereikbaar onder een omgeving — /environments/{environmentId}/api/par/v1/invoices — omdat een client-credentials-token een tenant noemt en niet een van diens omgevingen. De kortere openbare vorm komt met de Gateway, die een hostnaam aan een omgeving koppelt.

Wat er wordt uitgevoerd​

Eén ding: het script. Een definitie noemt een route en de BS Lang die erop antwoordt, en er valt verder niets te kiezen. Het script leest het verzoek met getInput, antwoordt met setOutput of setBody, kiest een status en inhoudstype met setStatus en setContentType, zet headers met setHeader, bereikt andere tabellen met de recordfuncties en weigert met throwValidationError.

Vroeger koos een definitie uit vier implementatiesoorten — een directe tabelbewerking, een Automation Flow, een Function en een aangepast plug-inbericht. Drie daarvan konden worden gedefinieerd maar nooit worden uitgevoerd, dus een aanroep antwoordde met 501 en noemde de iteratie waarop hij wachtte, en alleen de vierde draaide. Dat is verdwenen. Een contract waarvan het gedrag de eigen code van de maker is, is eenvoudiger uit te leggen, eenvoudiger te versioneren, en de enige van de vier die iets kon uitdrukken waarop het platform niet had geanticipeerd — en er is geen 501 meer, omdat er geen niet-aangesloten soort meer over is die er een kan opleveren.

Logica die het waard is om tussen twee API's te delen, is een oplossingsscript, op naam geïmporteerd, geen vijfde soort.

Regels die een schema niet kan uitdrukken​

Bij het opslaan van de definitie gecontroleerd, naast het schema hierboven:

  • Het gedeclareerde responseContentType is er een dat het platform kan serveren.
  • Een route voor Read, Update of Delete declareert {id} — dat is de enige plek waar de id van de record vandaan kan komen.
  • Een werkwoord zonder body heeft geen aanvraagschema. Een werkwoord met body mag het null laten.
  • De aanvraag- en antwoordschema's compileren als JSON Schema.
  • Het script is te parsen als BS Lang, en elke module die het importeert, verwijst naar een oplossingsscript.

Voorbeeld​

{
"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);"
}

De body wordt voordat het script draait tegen requestSchema gecontroleerd, dus getInput leest een vorm die al is gevalideerd in plaats van een vorm waartegen elk script zich zelf moet verdedigen.

Een invoer kan een object of een lijst zijn​

getInput geeft het script wat de eigenschap bevat — een tekenreeks, een getal, een booleaanse waarde, of een object of array in zijn geheel, die een script doorloopt per lid (f.fileName) en met length(). Een contract dat in zijn requestSchema een gestructureerde invoer beschrijft, kan die dus ook echt lezen:

"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 }
}
}

Tot portals-ronde 10 kwam een object binnen als ruwe JSON-tekst, die BS Lang op geen enkele manier kan parsen — dus een maker die de gewenste vorm declareerde, was slechter af dan iemand die haar platsloeg tot losse eigenschappen, en dat is het tegenovergestelde van waar een schema voor dient. De nestingdiepte is begrensd door de eigen limiet van 64 niveaus van de JSON-parser, nog voordat iets hiervan wordt bereikt.

Wat het nooit doet​

Een aangepaste API verruimt de toegang niet. De recordfuncties van het script gaan door dezelfde deur als de eigen schermen van het platform en draaien als de aanroeper, dus diens rechten op een tabel en het gegevensbeleid daarachter bepalen wat er terugkomt. Een zorgvuldig samengesteld contract geeft een extern systeem een stabiele blik, nooit een bredere.