[Nom de la Pages Function]
[Nom de la Pages Function]
Documentation technique
Type de ressource : Cloudflare Pages Function (Endpoint SSR Astro / API Route)
Fichier source :src/pages/api/[endpoint].tsoufunctions/[path].ts
Statut : [Actif / Déprécié / En développement]
1. Vue d’overview
1.1. Description
[Description concise du rôle de cette Pages Function dans l’architecture SSR du site]
1.2. Objectif
- [Objectif principal : ex. fournir un endpoint SSR pour le mode Preview Sanity]
- [Bénéfice pour l’architecture : ex. exécution Edge sans infrastructure serveur dédiée]
1.3. Caractéristiques
| Propriété | Valeur |
|---|---|
| Type | [Endpoint API / Route SSR / Middleware] |
| Méthode HTTP | [GET / POST / PUT / DELETE / Les deux] |
| Route publique | /api/[endpoint] ou /[path] |
| Mode de rendu | SSR (Server-Side Rendering) |
| Cache | [Voir §7] |
| Authentification | [Aucune / Cookie / Token / Les deux] |
2. Architecture et flux
2.1. Diagramme de flux
[Requête client] │ ▼[Cloudflare Pages] │ • Routage vers la Pages Function │ • Injection des bindings (Env) ▼[Pages Function] │ • Validation des entrées │ • Logique métier │ • Interaction avec les bindings (KV, R2, Queue) ▼[Services tiers / Storage] │ • Sanity API / CRM / R2 / KV ▼[Réponse HTTP]2.2. Positionnement dans l’architecture
[Client] │ ▼[Middleware Global (Worker L3)] ← Sécurité, CSP, cookies │ ▼[Cloudflare Pages] │ ▼[Pages Function (SSR)] ← Cette ressource │ ▼[Astro SSR / Services tiers]2.3. Composants impliqués
| Composant | Rôle | Type |
|---|---|---|
| Pages Function | Endpoint SSR | Cloudflare Pages |
| Middleware Global | Sécurité + Preview cookie | Worker L3 |
| Bindings Cloudflare | KV / R2 / Queue | Services Cloudflare |
| Astro SSR | Rendu des pages dynamiques | Framework |
3. Configuration
3.1. Emplacement des fichiers
# Option 1 : Routes API Astro (recommandé)src/pages/├── api/│ ├── preview.ts # Activation du mode Preview│ ├── exit-preview.ts # Sortie du mode Preview│ ├── contact.ts # Endpoint formulaire│ └── [autre-endpoint].ts
# Option 2 : Functions directory (pour routes non-Astro)functions/├── api/│ ├── webhook.ts│ └── [autre-endpoint].ts3.2. Variables d’environnement requises
| Variable | Description | Obligatoire | Valeur par défaut |
|---|---|---|---|
| [Variable] | [Description] | [✅/❌] | [Valeur] |
3.3. Bindings Cloudflare disponibles
| Binding | Type | Nom | Description |
|---|---|---|---|
| [Nom] | [KV / R2 / Queue / Secret] | [Binding name] | [Description] |
3.4. Configuration dans astro.config.mjs
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({ output: 'hybrid', // SSG par défaut, SSR explicite adapter: cloudflare({ platformProxy: { enabled: true, // Facilite le développement local avec Wrangler }, }),});4. Implémentation
4.1. Variante : Endpoint API (POST)1/2
4.1. Variante : Endpoint API (POST)2/2
import type { APIContext } from 'astro';
export const prerender = false; // SSR obligatoire
export async function POST({ request, locals }: APIContext) { try { const body = await request.json();
// Validation des entrées if (!body.email || !body.message) { return new Response(JSON.stringify({ error: 'Champs manquants' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); }
// Interaction avec les bindings await locals.runtime.env.SUBMISSIONS_QUEUE.send({ email: body.email, message: body.message, submittedAt: new Date().toISOString(), });
return new Response(JSON.stringify({ success: true }), { status: 200, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', }, }); } catch (error) { console.error('Erreur API contact:', error); return new Response(JSON.stringify({ error: 'Erreur serveur' }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); }}4.2. Variante : Endpoint Preview (GET)
import type { APIContext } from 'astro';
export const prerender = false;
export async function GET({ request, cookies, redirect }: APIContext) { const url = new URL(request.url); const secret = url.searchParams.get('secret'); const slug = url.searchParams.get('slug') || '/';
// Validation du secret (déjà faite par le Middleware, mais double vérification) if (!secret || secret !== import.meta.env.SANITY_PREVIEW_SECRET) { return new Response('Jeton invalide', { status: 401 }); }
// Pose du cookie de preview cookies.set('sanity-preview', 'true', { path: '/', httpOnly: true, secure: true, sameSite: 'lax', maxAge: 3600, // 1 heure });
// Redirection vers la page demandée return redirect(slug, 307);}4.3. Variante : Endpoint Exit Preview (GET)
import type { APIContext } from 'astro';
export const prerender = false;
export async function GET({ cookies, redirect }: APIContext) { // Suppression du cookie de preview cookies.delete('sanity-preview', { path: '/' });
// Redirection vers l'accueil return redirect('/', 302);}4.4. Variante : Page SSR dynamique
---export const prerender = false; // SSR obligatoire
import ContactForm from '../components/interactive/ContactForm';import BaseLayout from '../layouts/BaseLayout.astro';
// Accès aux bindings via locals.runtime.envconst env = Astro.locals.runtime.env;
// Logique SSR (si nécessaire)const siteConfig = await env.KV_BINDING.get('site-config', 'json');---
<BaseLayout title="Contact" description="Formulaire de contact"> <ContactForm apiEndpoint="/api/contact" /></BaseLayout>5. Accès aux bindings Cloudflare
5.1. Via locals.runtime.env (Astro)
// Dans une page ou endpoint Astroexport async function GET({ locals }: APIContext) { const env = locals.runtime.env;
// Accès KV const value = await env.KV_BINDING.get('key');
// Accès R2 const object = await env.R2_BUCKET.get('path/to/file');
// Accès Queue await env.QUEUE_BINDING.send({ data: 'value' });
// Accès Secret const secret = env.SECRET_NAME;
return new Response('OK');}5.2. Via env (Pages Functions standalone)
export interface Env { KV_BINDING: KVNamespace; R2_BUCKET: R2Bucket; QUEUE_BINDING: Queue; SECRET_NAME: string;}
export const onRequestPost: PagesFunction<Env> = async (context) => { const { env, request } = context;
// Utilisation des bindings await env.KV_BINDING.put('key', 'value');
return new Response('OK');};6. Sécurité
6.1. Validation des entrées
- ✅ Valider tous les paramètres d’URL et query strings
- ✅ Valider les corps de requête (JSON, FormData)
- ✅ Utiliser des schémas de validation (Zod, Yup)
- ✅ Sanitiser les entrées avant traitement
6.2. Protection CSRF
// Vérification du token CSRF (si applicable)const csrfToken = request.headers.get('x-csrf-token');if (!csrfToken || csrfToken !== expectedToken) { return new Response('CSRF token invalide', { status: 403 });}6.3. Rate Limiting
// Rate limiting simple via KV (si nécessaire)const ip = request.headers.get('cf-connecting-ip');const rateLimitKey = `rate-limit:${ip}`;const currentCount = await env.KV_BINDING.get(rateLimitKey, 'json') || 0;
if (currentCount >= 10) { // 10 requêtes max return new Response('Too Many Requests', { status: 429 });}
await env.KV_BINDING.put(rateLimitKey, currentCount + 1, { expirationTtl: 60 });6.4. Règles de sécurité
- ✅ Ne jamais exposer les secrets dans les réponses
- ✅ Valider cryptographiquement les webhooks (HMAC)
- ✅ Utiliser des comparaisons à temps constant pour les secrets
- ✅ Limiter les méthodes HTTP autorisées (GET, POST uniquement)
- ✅ Appliquer une CSP stricte (via Middleware)
7. Stratégie de cache
7.1. En-têtes de cache par type
| Type de route | Cache-Control | CDN-Cache-Control | Rationale |
|---|---|---|---|
| Endpoint API (GET) | public, max-age=60 |
max-age=300 |
Données semi-dynamiques |
| Endpoint API (POST) | no-store |
no-store |
Jamais en cache |
| Page SSR Preview | no-store, no-cache, must-revalidate, private |
no-store |
Contenu draft |
| Page SSR standard | public, max-age=60 |
max-age=300, stale-while-revalidate=86400 |
Contenu semi-dynamique |
7.2. Implémentation
// Dans un endpoint ou page SSRAstro.response.headers.set('Cache-Control', 'public, max-age=60');Astro.response.headers.set('CDN-Cache-Control', 'max-age=300, stale-while-revalidate=86400');
// Pour les réponses POSTreturn new Response(JSON.stringify(data), { headers: { 'Cache-Control': 'no-store', 'Content-Type': 'application/json', },});8. Performance et limites
8.1. Limites Cloudflare Pages Functions
| Limite | Valeur | Stratégie d’optimisation |
|---|---|---|
| Mémoire RAM | 128 MB | Streaming, URLs présignées |
| CPU time | 30s (Free) / 50s (Paid) | Optimisation des traitements |
| Nombre de sous-requêtes | 50 | Minimiser les appels externes |
| Taille requête/réponse | 100 MB | Streaming pour gros volumes |
8.2. Optimisations appliquées
- ✅ Utiliser le streaming HTTP pour les gros volumes
- ✅ Déporter les uploads lourds via URLs présignées
- ✅ Mettre en cache les données fréquemment lues (KV)
- ✅ Minimiser le nombre de sous-requêtes
- ✅ Utiliser
stale-while-revalidatepour les pages SSR
9. Monitoring et observabilité
9.1. Logs structurés
console.log(JSON.stringify({ function: 'api-contact', method: 'POST', status: 'success', duration: 123, metadata: { email: 'user@example.com' }}));9.2. Métriques clés
| Métrique | Seuil d’alerte | Canal |
|---|---|---|
| Taux d’erreur 5xx | > 1% sur 5 min | Slack |
| Latence p95 | > 500ms | Slack |
| Nombre de requêtes | > 1000/min |
9.3. Commandes de debug
# Logs en temps réelwrangler pages deployment tail --project-name=[project]
# Liste des déploiementswrangler pages deployment list --project-name=[project]10. Déploiement
10.1. Pipeline de déploiement
# Build Astro (inclut les Pages Functions)npm run build
# Déploiement automatique via Cloudflare Pages# (déclenché par git push ou webhook)10.2. Rollback
# Via le Dashboard Cloudflare# Cloudflare Dashboard → Pages → [Project] → Deployments → Rollback10.3. Tests manuels
# Test en localnpm run dev
# Test d'un endpointcurl -X POST http://localhost:4321/api/contact \ -H "Content-Type: application/json" \ -d '{"email": "test@example.com", "message": "Test"}'
# Test en productioncurl -X POST https://votresite.com/api/contact \ -H "Content-Type: application/json" \ -d '{"email": "test@example.com", "message": "Test"}'11. Débogage et troubleshooting
11.1. Problèmes courants
| Symptôme | Cause probable | Solution |
|---|---|---|
| 500 Internal Server Error | Erreur dans le code | Vérifier les logs Cloudflare |
| 404 Not Found | Route mal configurée | Vérifier l’emplacement du fichier |
| Bindings non disponibles | Configuration wrangler.toml manquante | Vérifier la configuration |
| Timeout | Traitement trop long | Optimiser ou déporter vers un Worker |
| CORS error | Headers manquants | Ajouter les headers CORS |
11.2. Erreurs fréquentes
| Code HTTP | Signification | Action |
|---|---|---|
| 400 | Requête malformée | Valider les entrées |
| 401 | Non authentifié | Vérifier les tokens/cookies |
| 403 | Accès refusé | Vérifier les permissions |
| 404 | Route non trouvée | Vérifier l’emplacement du fichier |
| 429 | Rate limit dépassé | Implémenter rate limiting |
| 500 | Erreur serveur | Vérifier les logs |
12. Bonnes pratiques
12.1. Architecture
- ✅ Préférer les routes API Astro (
src/pages/api/) aux Functions standalone - ✅ Centraliser la logique métier dans des services réutilisables
- ✅ Valider toutes les entrées (Zod, schémas custom)
- ✅ Gérer explicitement les erreurs (try/catch)
- ✅ Logger de manière structurée (JSON)
12.2. Code
- ✅ Typer strictement les bindings et les réponses
- ✅ Utiliser
export const prerender = falsepour les routes SSR - ✅ Retourner des réponses JSON avec les bons headers
- ✅ Ne jamais exposer les secrets dans les réponses
- ✅ Utiliser les bindings Cloudflare plutôt que des appels HTTP internes
12.3. Sécurité
- ✅ Valider cryptographiquement les webhooks (HMAC)
- ✅ Utiliser des comparaisons à temps constant pour les secrets
- ✅ Appliquer une CSP stricte (via Middleware)
- ✅ Limiter les méthodes HTTP autorisées
- ✅ Implémenter un rate limiting si nécessaire
12.4. Performance
- ✅ Utiliser le streaming HTTP pour les gros volumes
- ✅ Déporter les uploads lourds via URLs présignées
- ✅ Mettre en cache les données fréquemment lues
- ✅ Minimiser le nombre de sous-requêtes
- ✅ Utiliser
stale-while-revalidatepour les pages SSR
13. Évolutions et roadmap
13.1. Améliorations planifiées
- [Évolution 1 : ex. Ajout de endpoints API pour le headless CMS]
- [Évolution 2 : ex. Implémentation de GraphQL API]
- [Évolution 3 : ex. Support de WebSocket pour le temps réel]
13.2. Limitations connues
- ❌ Les Pages Functions ont une limite de 128 MB de RAM
- ❌ Le CPU time est limité à 30s (Free) / 50s (Paid)
- ❌ Pas de support natif pour WebSocket (nécessite Workers)
- ❌ Les bindings ne sont pas disponibles en mode SSG
14. Ressources liées1/2
- Documentation Cloudflare Pages : Functions
- Documentation Astro : Cloudflare Adapter
- DAT L2 : §2.1 - Mode Hybride & Adaptateur Cloudflare
- DAT L3 : §4 - Pages Functions Astro (SSR)
- Pages Functions associées :
src/pages/api/preview.tssrc/pages/api/exit-preview.tssrc/pages/api/contact.ts
14. Ressources liées2/2
- Fichiers concernés :
src/pages/api/*.tsfunctions/**/*.ts(si applicable)astro.config.mjs
15. Historique des modifications1/2
| Date | Version | Description | Auteur |
|---|---|---|---|
| YYYY-MM-DD | 1.0 | Création initiale | [Nom] |
| YYYY-MM-DD | 1.1 | [Modification] | [Nom] |
15. Historique des modifications2/2
---
**Points clés de ce template :**- Couvre les deux approches (routes API Astro et Functions standalone)- Détaille l'accès aux bindings Cloudflare via `locals.runtime.env`- Inclut la stratégie de cache par type de route- Documente les limites Cloudflare et les stratégies d'optimisation- Fournit les commandes de debug et de rollback- Sépare clairement les bonnes pratiques par catégorie (architecture, code, sécurité, performance)