Envoyer un email transactionnel avec Nuxt et Nitro

Nuxt permet de garder l’intégration entièrement dans server/. Le navigateur déclenche une action produit ; Nitro utilise la clé privée, appelle Spore et retourne immédiatement l’identifiant du message mis en file.

Le flux cible

Une clé dans runtimeConfig, un client serveur partagé et un endpoint Nitro protégé. La clé n’entre jamais dans runtimeConfig.public et n’est jamais incluse dans le bundle Vue.

1. Installer le SDK

pnpm add @lalternative/spore-sdk
SPORE_API_KEY=spore_api_key_here

2. Déclarer la configuration privée

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    sporeApiKey: process.env.SPORE_API_KEY,
  },
})

N’ajoutez pas sporeApiKey dans public : tout ce qui s’y trouve est lisible par le navigateur.

3. Initialiser le client dans Nitro

// server/utils/spore.ts
import { configureSporeClient, getSporeAPI } from '@lalternative/spore-sdk'
 
let configured = false
 
export function getSporeClient() {
  if (!configured) {
    const config = useRuntimeConfig()
    if (!config.sporeApiKey) throw new Error('SPORE_API_KEY is required')
    configureSporeClient({ apiKey: config.sporeApiKey })
    configured = true
  }
 
  return getSporeAPI()
}

4. Envoyer depuis un endpoint serveur

// server/api/welcome.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody<{ userId?: string; email?: string }>(event)
 
  if (!body.userId || !body.email) {
    throw createError({ statusCode: 400, statusMessage: 'userId and email are required' })
  }
 
  // Vérifiez ici la session et rechargez l’utilisateur depuis votre base.
  const result = await getSporeClient().sendEmail(
    {
      from: 'hello@votredomaine.fr',
      to: [body.email],
      subject: 'Bienvenue',
      html: '<p>Votre espace est prêt.</p>',
    },
    { headers: { 'Idempotency-Key': `welcome:${body.userId}` } },
  )
 
  return { messageId: result.messageId, status: result.status }
})

En production, protégez cet endpoint et ne prenez pas l’adresse finale pour vérité depuis le navigateur. Chargez-la depuis votre propre utilisateur afin d’éviter qu’un appel détourné transforme la route en relais d’envoi.

5. Retenter sans doubler

Nitro, votre queue ou votre hébergeur peut rejouer la requête. La clé d’idempotence stable welcome:<userId> garantit qu’une action métier reste un seul email. Le 202 confirme la mise en file ; les webhooks confirment ensuite la livraison, le bounce ou l’échec définitif.

Continuez avec la documentation du SDK TypeScript ou la référence de sendEmail.