# Bibliothèque `easyapi` — créer des API REST en BERKIOS CODE — v1.1.0

Source : https://docs.berkios.online/modules/easyapi/ · Section : Modules · Documentation officielle BERKIOS (https://docs.berkios.online)


27 commandes pour définir une **API REST** dans un script, la **tester sans réseau** (ou contre un vrai serveur), en tirer la description **OpenAPI 3.1** et **exporter un serveur Node.js autonome** (un seul fichier, aucune dépendance).
Chaque commande existe en **anglais**, **français**, **espagnol** et **swahili** (noms swahili non relus par des locuteurs natifs). Permissions demandées : `fichiers` (pour `save-server` et `save-openapi`, qui utilisent `telecharger`) et `reseau` (pour `call-api` sur un serveur désigné par `set-server`).
Ce sont de simples macros vers les 19 commandes intégrées de la catégorie **« API REST »** (`api-creer`, `api-ressource`, `api-route`… voir la leçon formation/08) : le moteur est dans le Studio (`studio/src/model/rest-api-engine.ts`), pas dans la bibliothèque.

## Une API en cinq lignes

```text
quand clic
  easy-api "Boutique" "produits" "nom:texte*, prix:nombre, sku:texte*!"
  protect-api
  make-key "mobile" "ecriture" "$cle"            # le secret s'affiche une seule fois
  call-api "$r" "POST" "/api/produits" '{"nom":"Stylo","sku":"A1","prix":2}' "$cle"
  status-code "$code"                            # 201
  save-server "serveur.mjs"                      # télécharge le serveur à déployer
```

`easy-api` crée l'API **Boutique** et la ressource `produits` avec ses six routes : `GET /api/produits`, `POST /api/produits`, `GET|PUT|PATCH|DELETE /api/produits/:id`.

## Ce que fait le moteur
- **Validation** à chaque écriture : types `texte`, `nombre`, `entier`, `booleen`, `date` (AAAA-MM-JJ), `email` ; `*` = obligatoire, `!` = unique. Champ inconnu, type faux ou doublon → `422` ou `409` avec le détail par champ.
- **Liste** : `?limit=` (1–1000, 50 par défaut), `?offset=`, **`?after=<id>` (pagination par curseur : la réponse porte `next_after` tant qu'il reste des pages)**, `?sort=champ` ou `?sort=-champ`, `?q=` (recherche dans les textes), `?champ=valeur` (filtre). Un filtre inconnu est une erreur `400`, pas ignoré.
- **Routes personnalisées** : `route`, `get-route`, `post-route`, `put-route`, `patch-route`, `delete-route`. La réponse est du JSON si elle commence par `{` ou `[`, sinon du texte ; `{param}`, `{query.x}`, `{body.x}`, `{method}`, `{path}`, `{date}` sont remplacés à chaque requête. Dans une réponse JSON, les valeurs injectées restent des chaînes : aucune injection possible.
- **Sécurité** : `protect-api` exige une clé (`X-API-Key` ou `Authorization: Bearer`) sur **toute** requête, même vers une route inconnue, sauf `/health`, `/openapi.json` et les routes marquées `public`. Rôles `lecture` (GET), `ecriture` (+ POST/PUT/PATCH/DELETE), `admin`. Les secrets ne sont **jamais** écrits dans la définition ni dans le serveur exporté.
- **CORS** : seules les origines listées par `allow-origin` sont autorisées ; `*` doit être écrit explicitement.
- **Débit** : `rate-limit N` → `429` avec `Retry-After` au-delà de N requêtes par minute et par client.
- **Import en masse** : `POST /ressource/bulk` (tableau JSON ou `{ "data": [...] }`, 1 000 enregistrements au plus par requête) est **atomique** : tout est créé ou rien, avec l'indice de l'enregistrement fautif. `seed-data` accepte jusqu'à 100 000 enregistrements par appel.
- Corps limité à 5 Mo, 1 000 000 d'enregistrements par ressource (`507` au-delà ; `EASYAPI_MAX_RECORDS` sur le serveur), 50 ressources, 200 routes, 20 clés ; en-têtes `Cache-Control: no-store` et `X-Content-Type-Options: nosniff`.

## Tester, documenter, déployer
- `call-api "$r" "GET" "/api/produits"` appelle l'API **dans le Studio, sans réseau**, avec le **même routeur** que le serveur exporté ; `status-code` et `response-header` lisent le code et les en-têtes.
- `set-server "https://api.monsite.com"` désigne un **vrai serveur** : `call-api` envoie alors de vraies requêtes HTTP (mêmes arguments, mêmes codes, `status-code` et `response-header` lisent la réponse du serveur). `set-server` sans argument revient à l'API d'essai : un même script sert à l'essai et à la production. Le navigateur du Studio impose le CORS : le serveur doit autoriser l'origine (`allow-origin`).
- `openapi` / `save-openapi "openapi.json"` : description OpenAPI 3.1 (Swagger UI, Postman, générateurs de clients). Elle est aussi servie par le serveur sur `/api/openapi.json`.
- `export-spec` / `import-spec` : sauvegarde et rechargement de la définition (sans données ni secrets).
- `save-server "serveur.mjs"` (ou `server-code "$code"`) : serveur **Node.js 18+**, sans dépendance.

```text
EASYAPI_KEYS="mobile:write:UnSecretLongEtAleatoire1234" PORT=3000 HOST=0.0.0.0 node serveur.mjs
```

| Variable | Rôle |
|---|---|
| `PORT` | port d'écoute (3000) |
| `HOST` | interface (127.0.0.1 ; `0.0.0.0` dans un conteneur ou derrière un proxy) |
| `DATA_FILE` | instantané JSON des données (`./donnees.json`) ; chaque modification est ajoutée au **journal** `DATA_FILE.journal` (écriture groupée toutes les 100 ms) puis fusionnée dans l'instantané au démarrage, à l'arrêt et quand le journal dépasse `EASYAPI_JOURNAL_MB` (64 par défaut) |
| `EASYAPI_MAX_RECORDS` | enregistrements maximum par ressource (1 000 000) ; compter environ 1 Ko de mémoire par enregistrement |
| `EASYAPI_KEYS` | clés `nom:role:secret` séparées par `;` (rôle `read`, `write` ou `admin`, secret de 16 caractères ou plus) |
| `TRUST_PROXY=1` | lit l'adresse du client dans `X-Forwarded-For` (proxy de confiance seulement) |

Après un arrêt brutal (kill, coupure), le journal est rejoué au redémarrage : on ne perd que les 100 dernières millisecondes. Une dernière ligne tronquée est ignorée ; une ligne corrompue au milieu du journal **empêche le démarrage** (rien n'est écrasé). Une API protégée **refuse de démarrer** sans clé. Placez toujours le serveur derrière **HTTPS** (Caddy, nginx…).

## Limites assumées
- Les données d'essai du Studio vivent en mémoire pour la session d'aperçu et ne sont pas enregistrées dans le projet : la définition, elle, est le script (ou `export-spec`). `easy-api` / `new-api` repartent de zéro.
- Pas de relations entre ressources ni de transactions (sauf le lot `/bulk`, atomique). **Gros volumes** : tout reste en mémoire (≈ 1 Ko par enregistrement, 1 000 000 au plus par ressource). Accès par id, unicité et filtres d'égalité (`?champ=valeur`) sont **indexés** ; le tri est mis en cache par champ ; la pagination par curseur (`after`) ne coûte pas plus cher à la page 10 000 qu'à la première. La recherche `?q=` parcourt les textes (linéaire) et un lot d'écritures simultanées reste sérialisé sur un seul processus. Au-delà du million, de plusieurs serveurs ou de besoins de requêtes complexes : une vraie base.
- L'instantané est relu avec `JSON.parse` : un fichier de données de plusieurs centaines de Mo demande autant de mémoire à la lecture.
- Pas de comptes utilisateurs : l'accès se règle par clés d'API.
- Le Studio ne peut pas ouvrir de port réseau : on teste avec `call-api`, puis on déploie le serveur exporté.
- **Flutter** : les 19 commandes `api-*` (donc toute la bibliothèque) existent dans l'application générée (`bridge/runtime/berkios_rest_api.dart`, moteur Dart pur) ; `set-server` + `call-api` y envoient de vraies requêtes HTTP (`dart:io`). Le moteur Dart est un **simulateur local en mémoire** : mêmes règles et mêmes messages, mais filtres d'égalité sans index. **Ce code Dart n'a pas été compilé** (ni Dart ni Flutter dans l'environnement de développement) : il est vérifié par des tests statiques et par le gabarit de serveur qu'il assemble ; à compiler au premier `flutter build`.

## Application client-serveur
L'accueil du Studio propose la carte **Application client-serveur** : projet autonome (client multiplateforme) marqué `architecture: "client-serveur"`, relié à un serveur qui héberge données et API REST. Ajoutez EasyAPI pour écrire l'API en BERKIOS CODE, exportez le serveur, puis appelez-le depuis le client.

## Exemple complet
`examples/application/api/client-serveur/` : une application de tâches (client BERKIOS avec easyapi, serveur Node exporté, clé d'API, import en masse, pagination par curseur). Essai sans serveur dans le Studio, puis contre `serveur.mjs`. Généré par `node scripts/build-api-client-serveur-example.mjs`.

## Commandes

| Noms (en · fr · es · sw) | Syntaxe | Effet |
|---|---|---|
| `easy-api · api-rapide · api-facil · api-rahisi` | `easy-api "Boutique" "produits" "nom:texte*, prix:nombre"` | Crée en une ligne une API complète : son nom et une première ressource avec ses 6 routes REST (liste, lecture, création, remplacement, modification, suppression) et la validation des champs. |
| `new-api · nouvelle-api · nueva-api · api-mpya` | `new-api "Nom" ["/prefixe"] ["1.0.0"]` | Commence une nouvelle API (efface la précédente) ; préfixe /api et version 1.0.0 par défaut. |
| `resource · ressource · recurso · rasilimali` | `resource "produits" "nom:texte*, prix:nombre, sku:texte*!"` | Déclare une ressource REST : GET/POST /produits et GET/PUT/PATCH/DELETE /produits/:id. Types : texte, nombre, entier, booleen, date, email ; * obligatoire, ! unique. |
| `route · chemin-api · ruta · njia` | `route "GET" "/salut/:nom" "Bonjour {nom}" [statut] [public]` | Ajoute une route personnalisée. Réponse JSON si le texte commence par { ou [, sinon texte brut ; {param}, {query.x}, {body.x} sont remplacés à chaque requête. |
| `get-route · route-get · ruta-get · njia-get` | `get-route "/chemin" "réponse" [statut] [public]` | Raccourci de route pour la méthode GET : get-route "/chemin" "réponse" équivaut à route "GET" "/chemin" "réponse". |
| `post-route · route-post · ruta-post · njia-post` | `post-route "/chemin" "réponse" [statut] [public]` | Raccourci de route pour la méthode POST : post-route "/chemin" "réponse" équivaut à route "POST" "/chemin" "réponse". |
| `put-route · route-put · ruta-put · njia-put` | `put-route "/chemin" "réponse" [statut] [public]` | Raccourci de route pour la méthode PUT : put-route "/chemin" "réponse" équivaut à route "PUT" "/chemin" "réponse". |
| `patch-route · route-patch · ruta-patch · njia-patch` | `patch-route "/chemin" "réponse" [statut] [public]` | Raccourci de route pour la méthode PATCH : patch-route "/chemin" "réponse" équivaut à route "PATCH" "/chemin" "réponse". |
| `delete-route · route-delete · ruta-delete · njia-delete` | `delete-route "/chemin" "réponse" [statut] [public]` | Raccourci de route pour la méthode DELETE : delete-route "/chemin" "réponse" équivaut à route "DELETE" "/chemin" "réponse". |
| `seed-data · donnees-api · datos-api · data-ya-api` | `seed-data "produits" '[{"nom":"Stylo","sku":"A1"}]'` | Insère des enregistrements de démonstration (tableau JSON) dans une ressource, validés comme par POST ; tout ou rien. |
| `protect-api · securiser-api · proteger-api · linda-api` | `protect-api [oui\|non]` | Exige une clé d'API (X-API-Key ou Authorization: Bearer) pour toute requête, sauf /health, /openapi.json et les routes « public ». |
| `make-key · creer-cle · crear-clave · tengeneza-ufunguo` | `make-key "mobile" "lecture\|ecriture\|admin" "Résultat"` | Crée une clé d'API et écrit son secret dans le résultat (à copier tout de suite : il n'est ni exporté ni relisible). |
| `allow-origin · autoriser-origine · permitir-origen · ruhusu-asili` | `allow-origin "https://monsite.com, https://app.monsite.com"` | Autorise des origines de navigateur précises (CORS). Aucune origine par défaut ; « * » doit être écrit explicitement. |
| `rate-limit · limiter-debit · limitar-tasa · punguza-kasi` | `rate-limit 120` | Limite le nombre de requêtes par minute et par client (0 = aucune) ; au-delà, la réponse est 429 avec Retry-After. |
| `set-server · serveur-api · servidor-api · seva-ya-api` | `set-server "https://api.monsite.com"` | Désigne le serveur d'API réel : call-api envoie alors ses requêtes HTTP à ce serveur au lieu de l'API d'essai ; sans argument, retour à l'API locale. |
| `call-api · appeler-api · llamar-api · piga-api` | `call-api "Résultat" "GET" "/api/produits" ["corps JSON"] ["cle"]` | Appelle l'API sans réseau (même routeur que le serveur exporté) et écrit le corps de la réponse ; le code HTTP se lit avec status-code. |
| `status-code · code-http · codigo-http · msimbo-http` | `status-code "Résultat"` | Écrit le code HTTP de la dernière réponse de call-api (200, 201, 404, 422…). |
| `response-header · entete-reponse · cabecera-respuesta · kichwa-jibu` | `response-header "Résultat" "Location"` | Écrit un en-tête de la dernière réponse de call-api (texte vide s'il est absent). |
| `list-routes · lister-routes · listar-rutas · orodhesha-njia` | `list-routes "Résultat"` | Liste les routes de l'API (« METHODE /chemin »), séparées par des virgules. |
| `openapi · documenter-api · documentar-api · andika-api` | `openapi "Résultat"` | Écrit la description OpenAPI 3.1 de l'API (JSON), utilisable avec Swagger UI, Postman ou un générateur de clients. |
| `count-records · compter-enregistrements · contar-registros · hesabu-rekodi` | `count-records "Résultat" "produits"` | Nombre d'enregistrements d'une ressource dans les données d'essai. |
| `clear-data · vider-donnees · vaciar-datos · futa-data` | `clear-data ["produits"]` | Vide les données d'essai d'une ressource (ou de toutes) sans toucher à la définition de l'API. |
| `export-spec · exporter-definition · exportar-definicion · hamisha-ufafanuzi` | `export-spec "Résultat"` | Écrit la définition de l'API en JSON (sans données ni secrets) pour la sauvegarder ou la partager. |
| `import-spec · importer-definition · importar-definicion · leta-ufafanuzi` | `import-spec "Source"` | Recharge une définition produite par export-spec (données d'essai remises à zéro ; clés à recréer). |
| `server-code · code-serveur · codigo-servidor · msimbo-seva` | `server-code "Résultat"` | Écrit le code d'un serveur Node.js autonome pour cette API : un fichier, aucune dépendance, données JSON sur disque, clés lues dans EASYAPI_KEYS. |
| `save-server · enregistrer-serveur · guardar-servidor · hifadhi-seva` | `save-server "serveur.mjs"` | Télécharge le serveur Node.js autonome de l'API dans un fichier. Lancez-le avec : node serveur.mjs |
| `save-openapi · enregistrer-openapi · guardar-openapi · hifadhi-openapi` | `save-openapi "openapi.json"` | Télécharge la description OpenAPI 3.1 de l'API dans un fichier. |
