Retour au blog

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.

C'est parti ! 🦅

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires