Definición de API personalizada
Id del esquema: custom-api@1
Esquema legible por máquina: custom-api.schema.json
La definición canónica de una API personalizada (sección 6.9): un contrato HTTP versionado que un entorno publica para sistemas externos a la plataforma, propiedad de una solución y que viaja en su paquete. Una definición se valida de forma idéntica tanto si procede del Centro de control de administración como de una llamada a la API, de un asistente de IA o de una importación de solución.
Forma
| Campo | Tipo | Obligatorio | Notas |
|---|---|---|---|
logicalName | string | sí | Con el prefijo del editor, en snake_case en minúsculas. Estable entre versiones. |
displayName | string | sí | El resumen de la operación OpenAPI. |
publisherPrefix | string | sí | También es un segmento de la ruta servida. |
version | integer | sí | 1–999. Se sirve como segmento de ruta v{version}. |
route | string | sí | Por debajo del segmento de versión, sin barra inicial. Un segmento entre llaves es un parámetro de ruta. |
verb | string | sí | Get, Post, Put, Patch, Delete. |
script | string | sí | El código BS Lang que ejecuta esta API. Hay una única implementación, y es esta. |
requiredScope | string | sí | El ámbito que debe tener el registro de aplicación del llamador. |
description | string | null | no | La descripción de la operación OpenAPI. |
requestSchema | object | null | no | Un JSON Schema, comprobado en cada llamada. Se rechaza para un verbo sin cuerpo. Null significa que los nombres de getInput del script son el contrato (publicado en OpenAPI; en una llamada de portal, una lista cerrada). |
responseSchema | object | null | no | Un JSON Schema que se publica en el documento OpenAPI. |
responseContentType | string | no | application/json, text/plain o text/html: con qué responde normalmente el contrato y lo que publica el OpenAPI generado. |
isEnabled | boolean | no | Una definición deshabilitada se almacena y se exporta, y devuelve 404. |
La identidad es un par
(logicalName, version) identifica la API; (publisherPrefix, version, verb, route) es su
dirección. par_approve_invoice v1 y v2 son un mismo contrato en dos momentos de su vida y
responden en paralelo mientras un socio de integración migra; por eso, una segunda definición que
comparte nombre y versión es un duplicado, mientras que una que comparte el nombre con una
versión nueva es la siguiente versión. Dos definiciones nunca pueden compartir dirección, se
llamen como se llamen.
La ruta servida
/api/{publisherPrefix}/v{version}/{route}
Hoy se accede a ella bajo un entorno —/environments/{environmentId}/api/par/v1/invoices—
porque un token de credenciales de cliente nombra un inquilino y no uno de sus entornos. La forma
pública más corta llegará con el Gateway, que asigna un nombre de host a un
entorno.
Qué se ejecuta
Una sola cosa: el script. Una definición nombra una ruta y el código BS Lang que la
responde, y no hay nada más que elegir. Lee la solicitud con getInput, responde con
setOutput o setBody, elige un estado y un tipo de contenido con setStatus y
setContentType, establece encabezados con setHeader, accede a otras tablas con las funciones
de registros y rechaza con throwValidationError.
Antes había que elegir entre cuatro tipos de implementación: una operación directa sobre una tabla, un flujo de automatización, una función y un mensaje de complemento personalizado. Tres de ellos podían definirse pero nunca se despachaban, así que una llamada respondía 501 indicando la iteración de la que dependía, y solo el cuarto se ejecutaba. Eso ya no existe. Un contrato cuyo comportamiento es el propio código del creador es más sencillo de explicar y de versionar, y era el único de los cuatro capaz de expresar algo que la plataforma no hubiera previsto; y ya no hay 501, porque no queda ningún tipo sin conectar que pueda producirlo.
La lógica que merece compartirse entre dos API es un script de solución, importado por su nombre, no un quinto tipo.
Reglas que un esquema no puede expresar
Se comprueban al guardar la definición, además del esquema anterior:
- El
responseContentTypedeclarado es uno que la plataforma puede servir. - Una ruta
Read,UpdateoDeletedeclara{id}: es el único lugar del que puede salir el id del registro. - Un verbo sin cuerpo no tiene esquema de solicitud. Un verbo con cuerpo puede dejarlo en null.
- Los esquemas de solicitud y de respuesta se compilan como JSON Schema.
- El script se analiza como BS Lang, y cada módulo que importa se resuelve en un script de solución.
Ejemplo
{
"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);"
}
El cuerpo se comprueba con requestSchema antes de que se ejecute el script, así que
getInput lee una forma que ya se ha validado, en lugar de una de la que cada script tenga que
defenderse por su cuenta.
Una entrada puede ser un objeto o una lista
getInput entrega al script lo que contenga la propiedad: una cadena, un número, un booleano, o
un objeto o una matriz completos, que el script recorre por miembro (f.fileName) y con
length(). Así, un contrato que describe una entrada estructurada en su requestSchema puede
leerla de verdad:
"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 }
}
}
Hasta la ronda 10 de portales, un objeto llegaba como su texto JSON sin procesar, que BS Lang no tiene forma de analizar; así, un creador que declaraba la forma que quería salía peor parado que uno que la aplanaba en propiedades sueltas, justo lo contrario de aquello para lo que sirve un esquema. El anidamiento está limitado por el propio límite de 64 niveles del analizador JSON, antes de llegar a nada de esto.
Lo que nunca hace
Una API personalizada no amplía el acceso. Las funciones de registros del script pasan por la misma puerta que las pantallas de la propia plataforma y se ejecutan como el llamador, así que son su permiso sobre una tabla y la directiva de datos que hay detrás los que deciden qué se devuelve. Un contrato seleccionado ofrece a un sistema externo una vista estable, nunca una más amplia.