Créer une API de demandes d’assistance

CloudflareBeginner
Pratiquer maintenant

Introduction

Un formulaire d’assistance a besoin d’une API capable de distinguer une demande valide, un JSON incorrect, une route inexistante et un service de tickets indisponible. Vous allez construire cette couche HTTP avec JavaScript, la tester localement, puis la déployer avec un Worker amont éphémère.

Utilisez votre propre compte Cloudflare d’apprentissage ainsi que les connaissances sur l’autorisation de l’appareil acquises dans le laboratoire de connexion. Ce laboratoire démarre dans une VM vierge avec Node.js 22.22.0 et Wrangler 4.131.1 installé localement au projet dans /home/labex/project/support-api. La connaissance de base des fonctions, objets et modules JavaScript est un prérequis ; le comportement HTTP et les requêtes asynchrones sont expliqués ici. Les deux Workers publics utilisent uniquement des données synthétiques. Le service amont fourni accuse réception des demandes, mais n’enregistre rien : il ne s’agit pas d’un système de tickets durable. Workers Free et un sous-domaine workers.dev suffisent ; aucune base de données, aucun domaine acheté ni aucune mise à niveau payante ne sont nécessaires pour cet exercice. Les demandes sont comptabilisées dans l’utilisation de Workers de votre compte.

Vous supprimerez les deux Workers et vous vous déconnecterez avant de terminer le laboratoire. Gardez le même terminal ouvert afin de conserver les variables du shell utilisées pour les noms de ressources et les URL.

Router les demandes selon le chemin et la méthode

Dans cette étape, vous allez associer à chaque URL prise en charge une méthode et une réponse explicites. Un chemin identifie l’opération ; une méthode décrit l’action. GET /health vérifie la disponibilité et POST /requests acceptera une demande d’assistance.

Accédez au projet préparé et vérifiez les outils :

cd /home/labex/project/support-api
node --version
npx wrangler --version

Vous devez obtenir Node v22.22.0 et Wrangler 4.131.1. L’installation est déjà terminée ; sur votre propre ordinateur, installez Node et utilisez npm install --save-dev wrangler@4.131.1 dans un projet. Utilisez npm ci pour reproduire un projet à partir de son fichier de verrouillage.

Générez un nom éphémère unique. openssl rand -hex 6 produit 12 caractères hexadécimaux aléatoires ; $(...) insère cette sortie et l’affectation du shell la conserve pour les commandes suivantes.

WORKER_NAME="labex-support-$(openssl rand -hex 6)"

Écrivez la configuration Wrangler standard. cat > file <<MARKER écrit les lignes suivantes jusqu’au marqueur de fermeture ; le marqueur non entouré de guillemets permet au shell de remplacer $WORKER_NAME.

cat > wrangler.jsonc <<CONFIG
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-09-14",
  "workers_dev": true,
  "preview_urls": false,
  "vars": {"UPSTREAM_URL": "http://127.0.0.1:8081"}
}
CONFIG

main identifie le gestionnaire, compatibility_date sélectionne le comportement du runtime et vars fournit une adresse amont non secrète via env. Pour le moment, cette adresse pointe vers un service local qui sera démarré plus tard. Les URL d’aperçu publiques sont désactivées afin de simplifier l’inventaire des ressources.

Écrivez le gestionnaire. Le marqueur JS entre guillemets conserve littéralement le code JavaScript. new URL(...).pathname extrait le chemin. L’expression ternaire choisit la méthode autorisée ; la réponse HTTP 405 annonce également cette méthode dans Allow. Response.json sérialise un objet et définit son type de contenu. Le gestionnaire async pourra attendre des opérations asynchrones dans les étapes suivantes.

cat > src/index.js <<'JS'
export default {
  async fetch(request, env) {
    const path = new URL(request.url).pathname;
    if (path !== '/health' && path !== '/requests') {
      return Response.json({error: 'not_found'}, {status: 404});
    }
    const allowed = path === '/health' ? 'GET' : 'POST';
    if (request.method !== allowed) {
      return Response.json({error: 'method_not_allowed'}, {
        status: 405, headers: {Allow: allowed}
      });
    }
    if (path === '/health') return Response.json({status: 'ok'});
    return Response.json({error: 'not_implemented'}, {status: 501});
  }
};
JS

Démarrez Wrangler localement en arrière-plan : > redirige la sortie, 2>&1 inclut les erreurs et & rend l’invite du terminal pendant que le serveur continue de s’exécuter.

npx wrangler dev --port 8080 > api.log 2>&1 &
cat api.log

Attendez que le journal indique que le serveur est prêt sur le port 8080. Réexécutez cat api.log si le démarrage est toujours en cours. curl -i inclut le statut et les en-têtes HTTP :

curl -i http://127.0.0.1:8080/health
curl -i http://127.0.0.1:8080/missing
curl -i http://127.0.0.1:8080/requests

Vous devez obtenir respectivement 200 avec {"status":"ok"}, 404 avec {"error":"not_found"}, puis 405 avec {"error":"method_not_allowed"} et Allow: POST. Ces réponses d’erreur sont intentionnelles. Utilisez le bouton de vérification pendant que le serveur fonctionne encore.

Analyser et valider les données JSON

Dans cette étape, vous allez rejeter les données incorrectes avant d’appeler un service amont. Le code HTTP 415 signifie que le type MIME n’est pas pris en charge, 400 que le JSON ne peut pas être analysé et 422 que les données analysées ne respectent pas le contrat. subject doit être une chaîne contenant entre 1 et 80 caractères après suppression des espaces superflus.

Remplacez le gestionnaire par cette version complète. headers.get lit le type MIME déclaré ; la séparation sur ; autorise un paramètre de charset. await request.json() attend l’analyse et consomme le corps une seule fois. Un bloc try/catch transforme une exception d’analyse en réponse prévisible. Le JSON peut aussi représenter null, des tableaux ou des nombres ; la validation vérifie donc la structure avant d’utiliser les méthodes de chaîne. trim() normalise la valeur acceptée de subject.

cat > src/index.js <<'JS'
export default {
  async fetch(request, env) {
    const path = new URL(request.url).pathname;
    if (path !== '/health' && path !== '/requests') {
      return Response.json({error: 'not_found'}, {status: 404});
    }
    const allowed = path === '/health' ? 'GET' : 'POST';
    if (request.method !== allowed) {
      return Response.json({error: 'method_not_allowed'}, {
        status: 405, headers: {Allow: allowed}
      });
    }
    if (path === '/health') return Response.json({status: 'ok'});
    const mediaType = (request.headers.get('content-type') || '').split(';')[0].trim().toLowerCase();
    if (mediaType !== 'application/json') {
      return Response.json({error: 'unsupported_media_type'}, {status: 415});
    }
    let body;
    try {
      body = await request.json();
    } catch {
      return Response.json({error: 'invalid_json'}, {status: 400});
    }
    if (!body || Array.isArray(body) || typeof body.subject !== 'string' ||
        body.subject.trim().length < 1 || body.subject.trim().length > 80) {
      return Response.json({error: 'invalid_subject'}, {status: 422});
    }
    const subject = body.subject.trim();
    return Response.json({subject}, {status: 201});
  }
};
JS

Wrangler recharge le Worker lorsque le code source change. Consultez cat api.log pour vérifier l’absence d’erreurs de compilation. Envoyez une demande valide : -H fournit un en-tête et --data fournit le corps et sélectionne POST. Les guillemets simples conservent les guillemets doubles du JSON dans le shell.

curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":"  Printer offline  "}'

Vous devez obtenir 201 et {"subject":"Printer offline"}. Il s’agit d’un accusé de réception en mémoire, pas d’un ticket enregistré. Testez trois chemins de rejet différents :

curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{'
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":"   "}'
curl -i http://127.0.0.1:8080/requests -H "Content-Type: text/plain" --data 'hello'

Vous devez obtenir 400 invalid_json, 422 invalid_subject et 415 unsupported_media_type. Testez également le JSON null, [] et {"subject":5} ; chacun doit retourner 422 sans provoquer d’exception. Utilisez le bouton de vérification ; il contrôle ces limites et conserve le comportement de santé et de routage.

Appeler un service amont et contenir ses erreurs

Dans cette étape, vous allez connecter l’API au simulateur de service de tickets fourni. Un service amont est une dépendance appelée par votre service. Le simulateur renvoie un ticket synthétique pour les sujets ordinaires et HTTP 503 pour le sujet spécial simulate-outage ; il n’enregistre jamais les demandes.

Examinez le code source fourni pour comprendre le service de test, puis configurez-lui une identité de Worker unique :

cat upstream/index.js
cat > upstream/wrangler.jsonc <<CONFIG
{
  "name": "${WORKER_NAME}-upstream",
  "main": "index.js",
  "compatibility_date": "2026-09-14",
  "workers_dev": true,
  "preview_urls": false
}
CONFIG

--config sélectionne cette seconde configuration. Utilisez le port 8081 et un port d’inspection distinct afin que les deux Workers locaux puissent fonctionner simultanément :

npx wrangler dev --config upstream/wrangler.jsonc --port 8081 --inspector-port 9230 > upstream.log 2>&1 &
cat upstream.log
curl -i http://127.0.0.1:8081/health

Attendez que le service soit prêt et vérifiez que vous obtenez 200 avec {"service":"support-upstream","status":"ok"}. Remplacez maintenant le gestionnaire principal par cette intégration complète. La fonction globale fetch effectue une requête sortante ; JSON.stringify encode le sujet validé. await attend la réponse. Les erreurs HTTP ne déclenchent pas d’exception : upstream.ok vérifie donc explicitement le statut ; catch gère séparément une connexion échouée ou une réponse JSON illisible. Le code HTTP 502 indique à notre client que la dépendance a échoué sans exposer le corps de sa réponse interne.

cat > src/index.js <<'JS'
export default {
  async fetch(request, env) {
    const path = new URL(request.url).pathname;
    if (path !== '/health' && path !== '/requests') {
      return Response.json({error: 'not_found'}, {status: 404});
    }
    const allowed = path === '/health' ? 'GET' : 'POST';
    if (request.method !== allowed) {
      return Response.json({error: 'method_not_allowed'}, {
        status: 405, headers: {Allow: allowed}
      });
    }
    if (path === '/health') return Response.json({status: 'ok'});
    const mediaType = (request.headers.get('content-type') || '').split(';')[0].trim().toLowerCase();
    if (mediaType !== 'application/json') {
      return Response.json({error: 'unsupported_media_type'}, {status: 415});
    }
    let body;
    try {
      body = await request.json();
    } catch {
      return Response.json({error: 'invalid_json'}, {status: 400});
    }
    if (!body || Array.isArray(body) || typeof body.subject !== 'string' ||
        body.subject.trim().length < 1 || body.subject.trim().length > 80) {
      return Response.json({error: 'invalid_subject'}, {status: 422});
    }
    const subject = body.subject.trim();
    try {
      const upstream = await fetch(`${env.UPSTREAM_URL}/tickets`, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({subject})
      });
      if (!upstream.ok) {
        return Response.json({error: 'upstream_unavailable'}, {status: 502});
      }
      const ticket = await upstream.json();
      return Response.json({ticket: ticket.ticket, subject}, {status: 201});
    } catch {
      return Response.json({error: 'upstream_unavailable'}, {status: 502});
    }
  }
};
JS
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":"Printer offline"}'
curl -i http://127.0.0.1:8080/requests -H "Content-Type: application/json" --data '{"subject":"simulate-outage"}'

Vous devez obtenir 201 avec {"ticket":"demo-1001","subject":"Printer offline"}, puis 502 avec {"error":"upstream_unavailable"}. Le diagnostic interne du simulateur ne doit pas apparaître. Le sujet est synthétique et cet endpoint n’a aucun effet durable. Utilisez le bouton de vérification avec les deux serveurs locaux en fonctionnement.

Ce laboratoire utilise HTTP standard pour vous faire pratiquer la communication avec un service externe. Un laboratoire ultérieur présente les liaisons de services pour les appels internes entre Workers. Les délais d’expiration bornés et les diagnostics plus détaillés sont étudiés dans Diagnostiquer les échecs d’un Worker. Le service de test renvoie de petites réponses limitées ; une API de production doit également limiter la taille des demandes et des réponses non fiables.

Déployer et tester l’API publique

Dans cette étape, vous allez déployer les deux Workers sur le même compte d’apprentissage et remplacer l’adresse amont locale par son URL publique. Arrêtez d’abord les deux processus locaux. Examinez jobs et utilisez le numéro réel de chaque processus ; les exemples supposent que l’API est 1 et le service amont 2.

jobs
kill %1 %2

Autorisez cette VM vierge. Cette autorisation identifie votre compte et permet de déployer et de supprimer des Workers. La dernière permission correspond à l’autorisation du cours sur les journaux, même si ce laboratoire n’a pas besoin d’un flux de journaux.

npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_tail:read

Ouvrez le lien affiché dans un navigateur, saisissez le code actuel de l’appareil, examinez les permissions de Wrangler, notamment l’accès en arrière-plan requis, sélectionnez uniquement votre compte d’apprentissage, puis autorisez l’accès. Revenez au terminal et attendez la fin de l’opération.

npx wrangler whoami --json

Vérifiez que loggedIn: true est présent, ainsi que le nom du compte et son ID réel dans accounts. Remplacez YOUR_ACCOUNT_ID ci-dessous par cet ID. Conservez les noms générés à l’étape 1 ; si une variable a été perdue, lisez la configuration enregistrée et restaurez-la au lieu de générer un autre nom de ressource.

cat > upstream/wrangler.jsonc <<CONFIG
{
  "name": "${WORKER_NAME}-upstream",
  "main": "index.js",
  "compatibility_date": "2026-09-14",
  "workers_dev": true,
  "preview_urls": false,
  "account_id": "YOUR_ACCOUNT_ID"
}
CONFIG
npx wrangler deploy --config upstream/wrangler.jsonc

Copiez l’URL workers.dev exacte affichée dans la sortie du déploiement. Réutilisez le sous-domaine déjà associé au compte. Si Wrangler propose d’enregistrer un sous-domaine pour la première fois, choisissez un nom disponible et suivez la confirmation ; ne modifiez pas un sous-domaine déjà associé au compte.

Réécrivez maintenant la configuration principale en remplaçant les deux espaces réservés par l’ID de votre compte et l’URL du service amont, sans barre oblique finale. global_fetch_strictly_public force les appels sortants de fetch() à utiliser le routage Internet public, y compris vers l’autre Worker du compte sur son sous-domaine workers.dev. Sans cette option, cet appel HTTP effectué dans la même zone peut échouer même si les deux Workers fonctionnent indépendamment. Cette option appartient à la configuration de l’API déployée ; le service de test local en boucle locale n’en a pas besoin. Consultez la documentation de l’API Fetch.

cat > wrangler.jsonc <<CONFIG
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-09-14",
  "compatibility_flags": ["global_fetch_strictly_public"],
  "workers_dev": true,
  "preview_urls": false,
  "account_id": "YOUR_ACCOUNT_ID",
  "vars": {"UPSTREAM_URL": "YOUR_UPSTREAM_URL"}
}
CONFIG
cat wrangler.jsonc
npx wrangler deploy

Copiez l’URL de l’API principale affichée dans la sortie du déploiement dans la variable ci-dessous :

API_URL="https://YOUR_API.YOUR_SUBDOMAIN.workers.dev"
curl -i "$API_URL/health"
curl -i "$API_URL/requests" -H "Content-Type: application/json" --data '{"subject":"Printer offline"}'
curl -i "$API_URL/requests" -H "Content-Type: application/json" --data '{"subject":"simulate-outage"}'
curl -i "$API_URL/requests" -H "Content-Type: application/json" --data '{'

Vous devez obtenir les mêmes contrats qu’en local : statut 200 pour la santé, 201 pour le ticket synthétique, 502 pour l’erreur du service amont et 400 pour le JSON incorrect. Attendez la propagation du nom d’hôte avant de réessayer en cas d’erreur de connectivité. Dans le Dashboard, sélectionnez le même compte d’apprentissage et ouvrez Compute → Workers & Pages. Recherchez les deux noms exacts et comparez leurs adresses avec la sortie du déploiement. Il s’agit d’un point de contrôle en lecture seule ; ne créez pas d’applications en double à cet endroit.

L’exemple ci-dessous montre l’API principale et le service -upstream correspondant. Dans la barre latérale gauche, développez Compute et choisissez Workers & Pages. Utilisez Search applications si votre compte contient d’autres projets. Comparez les noms complets générés et les adresses affichées dessous avec les deux sorties de déploiement ; votre suffixe aléatoire et le sous-domaine de votre compte seront différents de ceux de cet exemple.

Workers and Pages showing the support API and its matching upstream Worker

Les deux ressources doivent apparaître dans le même compte sélectionné. Leur présence confirme où elles ont été déployées ; les réponses HTTP ci-dessus confirment que l’API fonctionne. Si l’un des noms manque, vérifiez le sélecteur de compte et la sortie du déploiement avant de réessayer. N’utilisez pas Create application pour dupliquer un déploiement effectué avec la CLI.

Utilisez le bouton de vérification. Il vérifie indépendamment que vous êtes propriétaire des deux Workers, que la liaison vers le service amont est déployée et que les réponses publiques positives et négatives sont correctes. Il envoie uniquement des demandes synthétiques sans état au simulateur de ce laboratoire.

Supprimer les deux Workers éphémères

Dans cette étape, vous allez supprimer l’API et son service amont pendant que l’autorisation est encore disponible, afin de vérifier le résultat. Ce sont les seules ressources cloud créées par ce laboratoire. Examinez les deux configurations avant la suppression :

cat wrangler.jsonc
cat upstream/wrangler.jsonc

Vérifiez le nom principal labex-support-... et le suffixe -upstream correspondant, avec le même ID de compte d’apprentissage. Supprimez d’abord l’API principale, puis le service amont. À chaque invite, vérifiez le nom exact et appuyez sur la seule touche y.

npx wrangler delete
npx wrangler delete --config upstream/wrangler.jsonc

Wrangler 4.131.1 peut supprimer un Worker, puis afficher une erreur d’authentification lors de la vérification des données KV des anciens Workers Sites, car cette autorisation ne donne pas accès à KV. Ce diagnostic précis ne prouve ni la réussite ni l’échec de la suppression. N’accordez pas de permissions supplémentaires uniquement pour le faire disparaître. Actualisez Workers & Pages et utilisez le bouton de vérification : un inventaire autorisé réussi doit confirmer que les deux noms sont absents. Les erreurs réseau ou d’autorisation ne permettent pas de conclure ; résolvez-les avant de poursuivre. Préservez les autres applications, votre compte d’apprentissage et son sous-domaine.

Déconnecter la VM

Dans cette étape, vous allez supprimer l’autorisation Wrangler de cette VM après la réussite de la vérification de la suppression des deux ressources. La déconnexion ne supprime pas les Workers, c’est pourquoi le nettoyage a lieu en premier.

npx wrangler logout
npx wrangler whoami --json

Vous devez obtenir explicitement "loggedIn": false. Une commande d’état non authentifiée peut se terminer avec un code différent de zéro ; c’est normal lorsque son résultat structuré indique clairement la déconnexion. Une erreur réseau n’est pas équivalente. Utilisez le bouton de vérification, puis terminez l’environnement LabEx. Votre connexion au navigateur et votre compte d’apprentissage restent disponibles pour les prochains laboratoires ; chaque nouvelle VM demandera sa propre autorisation.

Résumé

Vous avez créé une API HTTP sensible à la méthode, analysé et validé du JSON, normalisé les données acceptées et transformé une défaillance du service amont en erreur publique prévisible. Vous avez testé les demandes acceptées et rejetées localement et sur Cloudflare, vérifié la propriété des deux déploiements, supprimé les ressources éphémères et déconnecté la VM.

Pour référence, consultez les documentations officielles de l’API Request, de l’API Response et de l’API Fetch.