Form Handler
Form Handler
Documentation technique
Type de ressource : Cloudflare Worker (Microservice FaaS)
Fichier source :services/form-handler/src/index.ts
Statut : Actif
1. Vue d’ensemble
1.1. Description
Worker Cloudflare pour la validation et la mise en queue des soumissions de formulaire. Valide les données entrantes via Zod, crée un objet de soumission avec métadonnées, et l’envoie dans la Queue Cloudflare pour un traitement asynchrone.
1.2. Objectif
- Valider les soumissions de formulaire côté serveur
- Découpler la validation du traitement asynchrone
- Utiliser une Queue Cloudflare pour le traitement en arrière-plan
- Fournir une réponse immédiate au frontend
1.3. Caractéristiques
| Propriété | Valeur |
|---|---|
| Type | Microservice métier |
| Trigger | HTTP fetch |
| Route publique | /api/submit, /health |
| Environnements | Development, Production |
| Consommateurs | Astro (frontend), Submission-processor (Queue consumer) |
2. Architecture et flux
[Entrée : Astro / Navigateur] │ • POST /api/submit ▼[Form Handler Worker] │ • Validation Zod (name, email, message) │ • Création de l'objet de soumission │ • Envoi dans la Queue ▼[Queue Cloudflare (submissions-queue)] │ ▼[Submission-processor Worker] │ • Création du document Sanity ▼[Sanity CMS]2.2. Positionnement dans la chaîne Edge
[Client] │ ▼[Astro Frontend] │ • POST /api/submit ▼[Form Handler Worker] │ ▼[Queue Cloudflare] │ ▼[Submission-processor Worker] │ ▼[Sanity CMS]2.3. Composants impliqués
| Composant | Rôle | Type |
|---|---|---|
| Zod | Validation des entrées | Bibliothèque |
| SUBMISSIONS_QUEUE | Queue asynchrone | Cloudflare Binding |
| Submission-processor | Consommateur de la Queue | Cloudflare Worker |
| Sanity API | Stockage des soumissions | Service tiers |
3. Configuration
3.1. Variables d’environnement
| Variable | Description | Obligatoire | Valeur par défaut |
|---|---|---|---|
ENVIRONMENT |
Environnement (dev/production) | ✅ | dev |
3.2. Bindings Cloudflare
| Binding | Type | Nom | Description |
|---|---|---|---|
| SUBMISSIONS_QUEUE | Queue | SUBMISSIONS_QUEUE | Queue pour les soumissions |
3.3. Configuration Wrangler (wrangler.toml)
name = "amn-form-handler"main = "src/index.ts"compatibility_date = "2025-07-18"compatibility_flags = ["nodejs_compat"]
[vars]ENVIRONMENT = "dev"
[[queues.producers]]queue = "submissions-queue-dev"binding = "SUBMISSIONS_QUEUE"
[env.dev]name = "amn-form-handler-dev"vars = { ENVIRONMENT = "dev" }
[[env.dev.queues.producers]]queue = "submissions-queue-dev"binding = "SUBMISSIONS_QUEUE"
[env.production]name = "amn-form-handler-prod"vars = { ENVIRONMENT = "production" }
[[env.production.queues.producers]]queue = "submissions-queue-prod"binding = "SUBMISSIONS_QUEUE"3.4. Injection des secrets
Aucun secret n’est injecté dans ce service.
4. Implémentation
4.1. Code de production (src/index.ts)1/2
4.1. Code de production (src/index.ts)2/2
import { z } from 'zod'
// Schema validation for form submissionconst SubmissionSchema = z.object({ name: z.string().min(2).max(100), email: z.string().email(), message: z.string().min(10).max(2000),})
export interface Env { SUBMISSIONS_QUEUE: Queue; ENVIRONMENT: string;}
export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url);
// Handle CORS preflight if (request.method === 'OPTIONS') { return handleCors(); }
// Handle POST requests for form submission if (request.method === 'POST' && url.pathname === '/api/submit') { return handleSubmit(request, env); }
// Health check endpoint if (request.method === 'GET' && url.pathname === '/health') { return new Response(JSON.stringify({ status: 'healthy', environment: env.ENVIRONMENT, timestamp: new Date().toISOString() }), { headers: { 'Content-Type': 'application/json' }, }); }
return new Response('Not Found', { status: 404 }); },};
async function handleSubmit(request: Request, env: Env): Promise<Response> { try { const body = await request.json();
// Validate input const validatedData = SubmissionSchema.parse(body);
// Create submission object with metadata const submission = { ...validatedData, submittedAt: new Date().toISOString(), id: crypto.randomUUID(), environment: env.ENVIRONMENT, };
// Send to queue for async processing await env.SUBMISSIONS_QUEUE.send(submission);
// Return immediate success response return new Response(JSON.stringify({ success: true, message: 'Form submitted successfully', id: submission.id, }), { headers: { 'Content-Type': 'application/json', ...corsHeaders(), }, });
} catch (error) { if (error instanceof z.ZodError) { return new Response(JSON.stringify({ success: false, error: 'Validation failed', details: error.issues, }), { status: 400, headers: { 'Content-Type': 'application/json', ...corsHeaders(), }, }); }
console.error('Submission error:', error); return new Response(JSON.stringify({ success: false, error: 'Internal server error', }), { status: 500, headers: { 'Content-Type': 'application/json', ...corsHeaders(), }, }); }}
function corsHeaders(): Record<string, string> { return { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', };}
function handleCors(): Response { return new Response(null, { status: 204, headers: corsHeaders(), });}4.2. Interfaces et types
// Schema Zodconst SubmissionSchema = z.object({ name: z.string().min(2).max(100), email: z.string().email(), message: z.string().min(10).max(2000),});
// Interface des bindingsexport interface Env { SUBMISSIONS_QUEUE: Queue; ENVIRONMENT: string;}
// Type de soumissiontype Submission = { name: string; email: string; message: string; submittedAt: string; id: string; environment: string;};5. Sécurité
5.1. Mécanismes de sécurité
| Mécanisme | Implémentation | Rationale |
|---|---|---|
| Validation Zod | Validation stricte des entrées | Prévention des injections |
| CORS | Headers CORS permissifs | Permettre les requêtes cross-origin |
| Aucun secret | Pas de secret injecté | Pas de données sensibles dans ce service |
5.2. Validation des entrées
- ✅ Validation Zod du nom (min 2, max 100)
- ✅ Validation Zod de l’email (format email)
- ✅ Validation Zod du message (min 10, max 2000)
5.3. Gestion des secrets
Non applicable - aucun secret n’est utilisé dans ce service.
6. Résilience et gestion des erreurs
6.1. Stratégie de retry
| Scénario | Stratégie | Rationale |
|---|---|---|
| Queue failure | Pas de retry | Simple retry dans le Queue consumer |
6.2. Safety Locks
Non implémenté - pas de lock configuré.
6.3. Gestion des timeouts
- ✅ Timeout global : Dépend de Cloudflare (30s Free / 50s Paid)
- ✅ Timeout par service tiers : Dépend de Cloudflare
7. Performance et limites
7.1. Limites Cloudflare Workers
| Limite | Valeur | Stratégie d’optimisation |
|---|---|---|
| Mémoire RAM | 128 MB | Pas de traitement lourd en mémoire |
| CPU time | 30s (Free) / 50s (Paid) | Validation rapide |
| Nombre de sous-requêtes | 50 | Pas de sous-requêtes |
| Taille requête/réponse | 100 MB | Payload de formulaire limité |
7.2. Optimisations appliquées
- ✅ Validation rapide (Zod)
- ✅ Mise en queue asynchrone (pas d’attente pour le traitement)
- ✅ Pas de chargement en mémoire
8. Monitoring et observabilité
8.1. Logs structurés
Non implémenté - logs console.error basiques.
8.2. Métriques clés
Non implémenté - aucune métrique ou alerte n’est configurée.
8.3. Commandes de debug
# Logs en temps réelwrangler tail form-handler
# Vérification des bindingswrangler deployments list9. Déploiement
9.1. Pipeline de déploiement
# Buildnpm run build --workspace=services/form-handler
# Déploiement stagingwrangler deploy --env staging
# Déploiement productionwrangler deploy --env production9.2. Rollback
# Liste des déploiementswrangler deployments list
# Rollback vers une version précédentewrangler deployments rollback --deployment-id=[id]9.3. Tests manuels
# Test HTTPcurl -X POST http://localhost:8789/api/submit \ -H "Content-Type: application/json" \ -d '{"name":"Test","email":"test@example.com","message":"Test message"}'
# Test health checkcurl http://localhost:8789/health10. Débogage et troubleshooting
10.1. Problèmes courants
| Symptôme | Cause probable | Solution |
|---|---|---|
| Validation échoue | Données invalides | Vérifier les règles Zod |
| Queue non disponible | Binding mal configuré | Vérifier wrangler.toml |
| CORS error | Headers CORS manquants | Vérifier la configuration CORS |
10.2. Erreurs fréquentes
| Code HTTP / Erreur | Signification | Action |
|---|---|---|
| 400 | Validation échouée | Corriger les données du formulaire |
| 500 | Erreur serveur | Vérifier les logs du Worker |
11. Bonnes pratiques
11.1. Architecture
- ✅ Découpler la validation du traitement asynchrone
- ✅ Utiliser les bindings Cloudflare plutôt que des appels HTTP internes
- ✅ Responsabilité unique (validation et mise en queue)
11.2. Code
- ✅ Typer strictement les bindings et les réponses
- ✅ Valider toutes les entrées (Zod)
- ✅ Gérer explicitement les erreurs (try/catch)
- ✅ Logger les erreurs
11.3. Sécurité
- ✅ Valider toutes les entrées côté serveur
- ✅ Utiliser Zod pour la validation
11.4. Performance
- ✅ Validation rapide
- ✅ Mise en queue asynchrone
12. Évolutions et roadmap
12.1. Améliorations planifiées
- Configuration de Logpush pour les logs
- Ajout de métriques et alertes
- Ajout de rate limiting
- Amélioration de la validation (CAPTCHA)
12.2. Limitations connues
- ❌ CORS permissif (
*) - à restreindre pour la production - ❌ Pas de rate limiting
- ❌ Pas de logs structurés
- ❌ Pas de monitoring configuré
13. Ressources liées1/2
- Documentation Cloudflare Workers : Getting Started
- Documentation Cloudflare Queues : Queues
- Documentation Zod : Zod
- Workers associés :
- submission-processor
13. Ressources liées2/2
- Fichiers concernés :
services/form-handler/src/index.tsservices/form-handler/wrangler.tomlservices/form-handler/package.json
14. Historique des modifications
| Date | Version | Description | Auteur |
|---|---|---|---|
| 2026-09-15 | 1.0 | Création initiale | Devin |