Skip to content

Type de ressource : Route Astro (Endpoint API)
Fichier source : src/pages/api/submit.ts
Statut : Actif

Endpoint API pour la soumission du formulaire de contact. Valide les données, envoie vers le form-handler Worker (local ou production via Cloudflare binding), et retourne le résultat. Partie intégrante du flux L1+L2+L3.

  • Recevoir les soumissions du formulaire de contact
  • Valider les donnĂ©es
  • Envoyer vers le backend L3 (Cloudflare Workers)
  • Retourner le rĂ©sultat au frontend
Propriété Valeur
URL publique /api/submit
Mode de rendu SSR
Type Endpoint API (JSON)
Méthode HTTP POST
Authentification requise Non
Cache No-store
[Formulaire soumis]
│ ‱ POST /api/submit
▌
[Endpoint /api/submit]
│ ‱ DĂ©tection de l'environnement (local vs production)
▌
[Local]
│ ‱ Fetch vers http://localhost:8789/test-direct
▌
[Form-handler Worker (local)]
│ ‱ Validation Zod
│ ‱ CrĂ©ation directe dans Sanity
▌
[Sanity CMS]
[Production]
│ ‱ Utilisation du service binding FORM_HANDLER
▌
[Form-handler Worker (Cloudflare)]
│ ‱ Validation Zod
│ ‱ Mise en queue dans submissions-queue
▌
[Queue Cloudflare]
│
▌
[Submission-processor Worker]
│ ‱ CrĂ©ation du document Sanity
▌
[Sanity CMS]
Composant RĂŽle Couche
api/submit.ts DĂ©finition de l’endpoint L2 (Astro)
Form-handler Worker Validation et mise en queue L3 (Cloudflare)
Submission-processor Worker Création document Sanity L3 (Cloudflare)
Queue Cloudflare Queue asynchrone L3 (Cloudflare)
Sanity CMS Stockage des soumissions L1 (Sanity)
import type { APIRoute } from 'astro';
import { env } from 'cloudflare:workers';
export const prerender = false;
export const POST: APIRoute = async (context) => {
const { request } = context;
try {
const data = await request.json();
const url = new URL(request.url);
const isLocal = url.hostname === 'localhost' ||
url.hostname === '127.0.0.1' ||
url.port === '4321';
let response: Response;
if (isLocal) {
// En local, on continue d'appeler le port local en HTTP classique
response = await fetch('http://localhost:8789/test-direct', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
} else {
// En production : Utilisation de l'import natif validé par Astro v6
const formHandler = (env as any).FORM_HANDLER;
if (!formHandler) {
const keys = Object.keys(env || {});
throw new Error(`Le Service Binding 'FORM_HANDLER' est introuvable. ÉlĂ©ments dispo dans env: [${keys.join(', ')}]`);
}
// Appel sécurisé via le binding avec l'URL interne
response = await formHandler.fetch('http://internal/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
}
const responseText = await response.text();
try {
const result = JSON.parse(responseText);
return new Response(JSON.stringify(result), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
} catch {
return new Response(JSON.stringify({
success: false,
error: 'Invalid JSON response from downstream worker',
details: responseText
}), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
}
} catch (error: any) {
console.error('Erreur réelle détectée lors de la soumission :', error.stack || error.message || error);
return new Response(JSON.stringify({
success: false,
error: 'Failed to process submission',
debugMessage: error.message,
debugStack: error.stack
}), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
};

Aucune requĂȘte GROQ n’est utilisĂ©e dans cet endpoint (la logique est dĂ©lĂ©guĂ©e aux Workers).

Champ Type Obligatoire Validation
name string ✅ Min 2, Max 100 (validĂ© par Worker)
email string ✅ Format email (validĂ© par Worker)
message string ✅ Min 10, Max 2000 (validĂ© par Worker)
Code HTTP Condition Réponse
200 SuccĂšs JSON avec success: true
400 Validation échouée JSON avec error: validation
500 Erreur serveur JSON avec error + debug info
Mode Cache-Control Rationale
API (POST) Non configurĂ© À configurer (no-store recommandĂ©)

Non implémenté - pas de headers de cache explicites.

Non applicable (endpoint API).

Aucun composant UI n’est utilisĂ© (endpoint API).

Condition Comportement
AccĂšs public Endpoint accessible sans authentification
  • ✅ Validation cĂŽtĂ© serveur via Zod (Worker)
  • ✅ Validation cĂŽtĂ© client (HTML5)

Non implémenté dans cet endpoint (à configurer via Middleware Cloudflare).

SymptĂŽme Cause probable Solution
Service binding introuvable FORM_HANDLER non configuré Vérifier apps/web/wrangler.toml
Erreur 500 Worker inaccessible Vérifier que le form-handler Worker est démarré
JSON invalide Worker retourne une réponse non-JSON Vérifier les logs du Worker
Terminal window
# Logs de développement
npm run dev
# Logs du form-handler Worker
wrangler tail form-handler
# Logs du submission-processor Worker
wrangler tail submission-processor
Terminal window
# Test de l'endpoint en local
curl -X POST http://localhost:4321/api/submit \
-H "Content-Type: application/json" \
-d '{"name":"Test","email":"test@example.com","message":"Test message"}'
  • ✅ DĂ©tection automatique de l’environnement (local vs production)
  • ✅ Utilisation des Cloudflare bindings en production
  • ✅ Fallback vers HTTP local en dĂ©veloppement
  • ✅ Logging des erreurs avec stack trace
  • ✅ Asynchrone (pas de blocage)
  • ✅ Queue pour le traitement en arriĂšre-plan
  • ✅ VĂ©rifier la configuration des bindings Cloudflare
  • ✅ Surveiller les logs des Workers
  • Configuration des headers de cache (no-store)
  • Ajout de rate limiting
  • Ajout de CAPTCHA
  • ❌ Headers de cache non configurĂ©s
  • ❌ Pas de rate limiting
  • ❌ Pas de CAPTCHA
  • ⚠ Utilisation de as any pour le binding FORM_HANDLER
  • Documentation Astro : Endpoints
  • Documentation Cloudflare : Service Bindings
  • Fichiers concernĂ©s :
    • src/pages/api/submit.ts
    • services/form-handler/src/index.ts
    • services/submission-processor/src/index.ts
    • apps/web/wrangler.toml
Date Version Description Auteur
2026-09-15 1.0 Création initiale Devin
Edit page

Last updated: