emailr_
Tous les articles
usecase·9 min

Email serverless : Modèles pour Lambda et Edge

serverlesslambdaedge

Les fonctions serverless ont des contraintes spécifiques pour l’email : cold starts, timeouts et absence d’état. Voici comment envoyer des emails efficacement depuis Lambda, des fonctions Edge et des environnements similaires.

Contraintes du serverless

  • Latence de cold start
  • Limites de temps d’exécution (souvent 10–30 secondes)
  • Pas de connexions persistantes
  • Exécution sans état
  • Tarification à l’invocation

Modèle d’envoi direct

Email simple avec Lambda

import { Emailr } from 'emailr';

const emailr = new Emailr(process.env.EMAILR_API_KEY);

export async function handler(event: APIGatewayEvent) {
  const { to, subject, html } = JSON.parse(event.body);
  
  try {
    const result = await emailr.emails.send({
      from: '[email protected]',
      to,
      subject,
      html
    });
    
    return {
      statusCode: 200,
      body: JSON.stringify({ id: result.id })
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Failed to send email' })
    };
  }
}

Gestion des cold starts

// Initialize outside handler for connection reuse
const emailr = new Emailr(process.env.EMAILR_API_KEY);

// Warm connections with provisioned concurrency
export const handler = async (event) => {
  // Handler code - connection already warm
};

Modèle basé sur une file d’attente

Pour des volumes élevés ou des emails non critiques, mettez en file d’attente plutôt que d’envoyer directement :

import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';

const sqs = new SQSClient({});

export async function queueEmail(event: APIGatewayEvent) {
  const email = JSON.parse(event.body);
  
  await sqs.send(new SendMessageCommand({
    QueueUrl: process.env.EMAIL_QUEUE_URL,
    MessageBody: JSON.stringify(email),
    MessageAttributes: {
      priority: {
        DataType: 'String',
        StringValue: email.priority || 'normal'
      }
    }
  }));
  
  return { statusCode: 202, body: JSON.stringify({ queued: true }) };
}

// Separate worker Lambda processes queue
export async function processEmailQueue(event: SQSEvent) {
  const results = await Promise.allSettled(
    event.Records.map(record => {
      const email = JSON.parse(record.body);
      return emailr.emails.send(email);
    })
  );
  
  // Failed messages return to queue automatically
  const failures = results.filter(r => r.status === 'rejected');
  if (failures.length > 0) {
    throw new Error(`${failures.length} emails failed`);
  }
}

Modèles pour fonctions Edge

Cloudflare Workers

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { to, subject, html } = await request.json();
    
    const response = await fetch('https://api.emailr.dev/emails', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${env.EMAILR_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ from: '[email protected]', to, subject, html })
    });
    
    return new Response(JSON.stringify({ sent: response.ok }), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};

Vercel Edge Functions

import { NextResponse } from 'next/server';

export const runtime = 'edge';

export async function POST(request: Request) {
  const { to, subject, html } = await request.json();
  
  const response = await fetch('https://api.emailr.dev/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.EMAILR_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ from: '[email protected]', to, subject, html })
  });
  
  return NextResponse.json({ sent: response.ok });
}

Gestion des timeouts

// Set aggressive timeouts for serverless
const emailr = new Emailr(process.env.EMAILR_API_KEY, {
  timeout: 5000 // 5 seconds max
});

export async function handler(event) {
  try {
    const result = await Promise.race([
      emailr.emails.send(email),
      new Promise((_, reject) => 
        setTimeout(() => reject(new Error('Timeout')), 8000)
      )
    ]);
    
    return { statusCode: 200, body: JSON.stringify(result) };
  } catch (error) {
    if (error.message === 'Timeout') {
      // Queue for retry instead of failing
      await queueForRetry(email);
      return { statusCode: 202, body: JSON.stringify({ queued: true }) };
    }
    throw error;
  }
}

Traitement par lots

// Process multiple emails efficiently
export async function batchHandler(event: SQSEvent) {
  const emails = event.Records.map(r => JSON.parse(r.body));
  
  // Batch API call if supported
  const result = await emailr.emails.sendBatch(emails);
  
  return {
    batchItemFailures: result.failed.map(f => ({
      itemIdentifier: f.messageId
    }))
  };
}

Bonnes pratiques

  1. Initialiser en dehors du handler - Réutiliser les connexions
  2. Utiliser des files d’attente pour le volume - Ne pas bloquer sur les envois
  3. Définir des timeouts - Ne pas dépasser les limites de la fonction
  4. Gérer les échecs avec élégance - Mettre en file d’attente pour réessayer
  5. Surveiller les cold starts - Utiliser la provisioned concurrency si nécessaire
  6. Garder des payloads petits - Minimiser le transfert de données

L’email serverless consiste à travailler dans les contraintes. Concevez pour le modèle d’exécution et vous aurez un email fiable et scalable.

e_

Écrit par l'équipe emailr

Nous construisons l'infrastructure email pour les développeurs

Prêt à commencer à envoyer ?

Obtenez votre clé API et envoyez votre premier email en moins de 5 minutes. Aucune carte de crédit requise.