Serverless et Edge Functions : Pourquoi 75% des Nouvelles Applications Utilisent Cette Architecture
Salut HaWkers, la façon dont nous déployons les applications a drastiquement changé ces dernières années. Le serverless n'est plus une nouveauté, mais les Edge Functions ont ajouté une nouvelle dimension en plaçant votre code géographiquement proche des utilisateurs. Aujourd'hui, 75% des nouvelles applications web naissent avec un composant serverless ou edge.
Mais quand utiliser chaque approche ? Quels sont les vrais trade-offs ? Et comment JavaScript et Node.js s'intègrent dans ce scénario ?
Le Panorama Actuel
En 2025, les principales plateformes ont considérablement étendu leurs offres edge et serverless :
Principaux Acteurs
Cloudflare Workers :
- 320+ points de présence globaux
- Démarrage en < 5ms (cold start)
- Support WebSockets, Durable Objects
- Prix agressif (100k requêtes gratuites/jour)
Vercel Edge Functions :
- Intégré avec Next.js, Nuxt, SvelteKit
- Déploiement automatique via Git
- Edge Middleware pour routing intelligent
- Cache edge granulaire
AWS Lambda@Edge / CloudFront Functions :
- Intégré à l'écosystème AWS
- Plus grand nombre de régions
- Triggers d'événements CDN
- Échelle massive prouvée
Deno Deploy :
- TypeScript natif
- Déploiement instantané
- Latence ultra basse
- Agnostique aux frameworks
Chiffres d'Adoption
Statistiques 2025 :
- 75% des nouvelles applications web utilisent le serverless
- 45% utilisent déjà les edge functions
- Réduction moyenne de 60% de la latence avec l'edge
- Coût 40-70% inférieur aux serveurs traditionnels
Comprendre les Différences
Serverless et Edge sont des concepts liés mais distincts.
Serverless Traditionnel (Lambda, Cloud Functions)
Votre code s'exécute dans un data center, mais vous ne gérez pas de serveurs :
// AWS Lambda - Handler classique
export const handler = async (event) => {
const { body } = event;
const data = JSON.parse(body);
// Traite les données
const result = await processData(data);
// Sauvegarde dans DynamoDB
await dynamoDB.put({
TableName: 'orders',
Item: result
}).promise();
return {
statusCode: 200,
body: JSON.stringify({ success: true, id: result.id })
};
};Caractéristiques :
- Cold starts peuvent atteindre 1-5 secondes
- Accès à tout l'écosystème cloud (databases, queues, etc.)
- Pas de limites strictes de mémoire/temps (configurable)
- Idéal pour le traitement lourd
Edge Functions
Votre code s'exécute dans des points de présence proches de l'utilisateur :
// Cloudflare Workers - Edge Function
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Logique de routing à l'edge
if (url.pathname.startsWith('/api/')) {
return handleApi(request, env);
}
// Personnalisation par géolocalisation
const country = request.cf?.country || 'US';
const content = await getLocalizedContent(country, env);
return new Response(content, {
headers: { 'Content-Type': 'text/html' }
});
}
};
async function getLocalizedContent(country: string, env: Env) {
// Récupération du KV Storage à l'edge
const cached = await env.CONTENT_KV.get(`home-${country}`);
if (cached) return cached;
// Fallback
return env.CONTENT_KV.get('home-default');
}Caractéristiques :
- Cold starts en < 50ms (généralement < 5ms)
- Limitations de runtime (CPU time, mémoire)
- APIs limitées (pas de filesystem, certaines APIs Node.js)
- Idéal pour la logique légère et la personnalisation
Cas d'Usage Pratiques
Quand Utiliser les Edge Functions
A/B Testing et Feature Flags :
// Vercel Edge Middleware
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Détermine le bucket d'A/B test à l'edge
const bucket = request.cookies.get('ab-bucket')?.value
|| (Math.random() < 0.5 ? 'A' : 'B');
const response = NextResponse.next();
// Définit le cookie si nouvel utilisateur
if (!request.cookies.get('ab-bucket')) {
response.cookies.set('ab-bucket', bucket, {
maxAge: 60 * 60 * 24 * 30 // 30 jours
});
}
// Rewrite vers la version correcte
if (bucket === 'B' && request.nextUrl.pathname === '/checkout') {
return NextResponse.rewrite(new URL('/checkout-new', request.url));
}
return response;
}
export const config = {
matcher: ['/checkout', '/product/:path*']
};Authentification et Protection :
// Validation JWT à l'edge
import { jwtVerify } from 'jose';
export default {
async fetch(request: Request, env: Env) {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
try {
const secret = new TextEncoder().encode(env.JWT_SECRET);
const { payload } = await jwtVerify(token, secret);
// Ajoute les infos user à la requête
const newHeaders = new Headers(request.headers);
newHeaders.set('X-User-Id', payload.sub as string);
return fetch(request, { headers: newHeaders });
} catch {
return new Response('Invalid token', { status: 401 });
}
}
};
Quand Utiliser le Serverless Traditionnel
Traitement Lourd :
// AWS Lambda pour traitement d'image
import sharp from 'sharp';
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
export const handler = async (event) => {
const { bucket, key } = event;
// Récupère l'image originale
const s3 = new S3Client({});
const original = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
// Traite avec Sharp (non disponible à l'edge)
const processed = await sharp(await original.Body.transformToByteArray())
.resize(800, 600, { fit: 'inside' })
.webp({ quality: 80 })
.toBuffer();
// Sauvegarde la version optimisée
await s3.send(new PutObjectCommand({
Bucket: bucket,
Key: key.replace(/\.\w+$/, '.webp'),
Body: processed,
ContentType: 'image/webp'
}));
return { processed: true };
};Intégration avec Bases de Données :
// Lambda avec connexion base de données
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10
});
export const handler = async (event) => {
const { userId, action, data } = JSON.parse(event.body);
const client = await pool.connect();
try {
await client.query('BEGIN');
// Transaction complexe
const result = await client.query(
'INSERT INTO orders (user_id, data) VALUES ($1, $2) RETURNING id',
[userId, data]
);
await client.query(
'UPDATE users SET last_order = NOW() WHERE id = $1',
[userId]
);
await client.query('COMMIT');
return {
statusCode: 200,
body: JSON.stringify({ orderId: result.rows[0].id })
};
} catch (e) {
await client.query('ROLLBACK');
throw e;
} finally {
client.release();
}
};
Architecture Hybride
L'approche la plus courante en 2025 est de combiner edge et serverless :
Pattern Recommandé
Utilisateur
|
v
[Edge Function] ─────────────────┐
│ │
│ - Validation auth │
│ - A/B testing │
│ - Geo routing │
│ - Vérification cache │
│ │
v v
[CDN Cache] <──── ou ────> [Origin/Lambda]
│ │
│ - Assets statiques │ - Logique métier
│ - Réponses cachées │ - Ops base de données
│ │ - Traitement lourd
v v
[Utilisateur] [Database/Storage]Exemple avec Next.js
// middleware.ts (Edge)
export function middleware(request: NextRequest) {
// Logique légère à l'edge
const country = request.geo?.country || 'US';
const response = NextResponse.next();
response.headers.set('X-Country', country);
return response;
}
// app/api/orders/route.ts (Serverless)
export async function POST(request: Request) {
// Logique lourde en serverless
const data = await request.json();
const order = await prisma.order.create({
data: {
...data,
country: request.headers.get('X-Country')
}
});
return Response.json(order);
}
Trade-offs et Considérations
Limitations de l'Edge
Restrictions courantes :
- CPU time limité (généralement 50-100ms)
- Mémoire limitée (128MB typique)
- Pas d'accès au filesystem
- Subset d'APIs Node.js
- Connexions base de données plus complexes
Quand NE PAS Utiliser l'Edge
Évitez l'edge pour :
- Requêtes complexes de base de données
- Traitement de fichiers volumineux
- Opérations qui prennent > 100ms
- Dépendances Node.js lourdes (sharp, puppeteer)
Coûts
| Approche | Coût Typique | Cold Start | Latence |
|---|---|---|---|
| Edge Function | $0.50/million req | < 5ms | 10-50ms |
| Lambda | $0.20/million req | 100-5000ms | 50-200ms |
| Container | $50-200/mois | 0ms | 20-100ms |
Conclusion
Serverless et Edge Functions ne sont pas des approches exclusives, mais complémentaires. L'Edge est idéal pour la logique légère, la personnalisation et la réduction de latence. Le serverless traditionnel reste la meilleure option pour le traitement lourd et l'intégration avec les bases de données.
En 2025, la majorité des applications bien architecturées utilisent les deux : edge pour la première couche de traitement et serverless pour les opérations plus complexes.
Si vous voulez en savoir plus sur les architectures JavaScript modernes, je recommande de consulter l'article sur Async/Await en JavaScript où nous explorons des patterns qui fonctionnent bien dans ces architectures.

