Skip to content

[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].ts ou functions/[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].ts

3.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

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

src/pages/api/contact.ts
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)

src/pages/api/preview.ts
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)

src/pages/api/exit-preview.ts
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

src/pages/contact.astro
---
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.env
const 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 Astro
export 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)

functions/api/webhook.ts
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 SSR
Astro.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 POST
return 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-revalidate pour 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 Email

9.3. Commandes de debug

Terminal window
# Logs en temps réel
wrangler pages deployment tail --project-name=[project]
# Liste des déploiements
wrangler pages deployment list --project-name=[project]

10. Déploiement

10.1. Pipeline de déploiement

Terminal window
# 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

Terminal window
# Via le Dashboard Cloudflare
# Cloudflare Dashboard → Pages → [Project] → Deployments → Rollback

10.3. Tests manuels

Terminal window
# Test en local
npm run dev
# Test d'un endpoint
curl -X POST http://localhost:4321/api/contact \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "message": "Test"}'
# Test en production
curl -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 = false pour 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-revalidate pour 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.ts
    • src/pages/api/exit-preview.ts
    • src/pages/api/contact.ts

14. Ressources liées2/2

  • Fichiers concernés :
    • src/pages/api/*.ts
    • functions/**/*.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)
Switch to normal mode

Contents