Tutorial
Enviar emails con Nuxt
Envía emails desde una ruta de API de servidor de Nuxt con el SDK de Node.js de Emailit, guarda la clave en el runtimeConfig privado y verifica los webhooks con h3.
En esta guía se explica cómo enviar emails desde una aplicación Nuxt 3. Llamas a Emailit desde una ruta de API de servidor, guardas la clave de API en el runtimeConfig privado y verificas los webhooks con las funciones auxiliares de h3.
Requisitos previos
- Nuxt 3 con un preset de servidor de Node.js 18 o posterior.
- Un dominio de envío verificado, por ejemplo
acme.com. - Una clave de API. Basta con una clave Sending Only limitada a tu dominio.
- Hasta que tu espacio de trabajo tenga acceso de producción, solo puedes enviar a las direcciones de email de las cuentas de los miembros del espacio de trabajo.
Instalar el SDK
npm install @emailit/nodeConfigurar la clave de API
Declara claves privadas en el runtime config. Las claves que están fuera de public solo están disponibles en el servidor:
export default defineNuxtConfig({
runtimeConfig: {
emailitApiKey: '',
emailitWebhookSecret: '',
},
});Nuxt las rellena a partir de las variables de entorno con el prefijo NUXT_:
NUXT_EMAILIT_API_KEY=secret_••••••••••••••••••••••••••••••••
NUXT_EMAILIT_WEBHOOK_SECRET=whsec_••••••••Añade una pequeña función auxiliar. Los archivos de server/utils se importan automáticamente en las rutas de servidor:
import type { H3Event } from 'h3';
import { Emailit } from '@emailit/node';
export function emailitClient(event: H3Event) {
return new Emailit(useRuntimeConfig(event).emailitApiKey);
}Si TypeScript indica que falta una declaración para @emailit/node, añade declare module '@emailit/node'; a un archivo .d.ts de tu proyecto.
Enviar un email
Crea una ruta de API de servidor. Esta ruta de un formulario de contacto envía a una dirección interna fija, así que los visitantes no pueden usarla para enviar emails a cualquiera:
export default defineEventHandler(async (event) => {
const { email, message } = await readBody(event);
if (typeof email !== 'string' || typeof message !== 'string' || !email.includes('@')) {
throw createError({ statusCode: 400, statusMessage: 'Invalid input' });
}
const sent = await emailitClient(event).emails.send({
from: 'Acme website <website@acme.com>',
to: 'support@acme.com',
reply_to: email,
subject: 'New contact form message',
text: message,
});
return { id: sent.id };
});Llámala desde una página o un componente con $fetch:
<script setup lang="ts">
const email = ref('');
const message = ref('');
const sent = ref(false);
async function submit() {
await $fetch('/api/contact', {
method: 'POST',
body: { email: email.value, message: message.value },
});
sent.value = true;
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="email" type="email" required />
<textarea v-model="message" required />
<button type="submit">Send</button>
<p v-if="sent">Thanks, we'll be in touch.</p>
</form>
</template>Para enviar una plantilla guardada en lugar de contenido en línea, pasa template (un alias o un ID tem_) y variables. Consulta Plantillas y Enviar un email.
Enviar por SMTP
Si prefieres SMTP, usa Nodemailer en una ruta de servidor. SMTP necesita un preset de servidor de Node.js; los presets edge y de workers no pueden abrir conexiones SMTP, así que en ellos usa la API.
import nodemailer from 'nodemailer';
export function mailer() {
return nodemailer.createTransport({
host: 'smtp.emailit.com',
port: 587,
secure: false,
requireTLS: true,
auth: { user: 'emailit', pass: useRuntimeConfig().emailitApiKey },
});
}Después, llama a await mailer().sendMail({ from, to, subject, html }) desde una ruta. Para otros puertos, consulta Configuración SMTP.
Recibir webhooks
Crea un webhook que apunte a https://your-app.com/api/webhooks/emailit. Verifica la firma con el cuerpo en bruto y después analiza el array de eventos:
import { createHmac, timingSafeEqual } from 'node:crypto';
export default defineEventHandler(async (event) => {
const rawBody = (await readRawBody(event)) ?? '';
const signature = getHeader(event, 'x-emailit-signature') ?? '';
const timestamp = getHeader(event, 'x-emailit-timestamp') ?? '';
const secret = useRuntimeConfig(event).emailitWebhookSecret;
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
const valid =
Number.isFinite(age) &&
age <= 300 &&
expected.length === signature.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
if (!valid) {
throw createError({ statusCode: 401, statusMessage: 'Invalid signature' });
}
const events = JSON.parse(rawBody) as Array<{ event_id: string; type: string; data: { object: any } }>;
for (const item of events) {
if (item.type === 'email.complained') {
// Stop emailing item.data.object.to
}
}
return { received: events.length };
});Devuelve un 2xx en menos de 30 segundos; las demás respuestas se reintentan. Consulta Firma de las peticiones.
Consejos para producción
- Define los secretos en tu hosting. Configura
NUXT_EMAILIT_API_KEYen la configuración de entorno de tu proveedor de hosting en lugar de desplegar un archivo.env. - Valida antes de enviar. Acepta un destinatario desde el navegador solo cuando sea la dirección del propio usuario que ha iniciado sesión, y limita la frecuencia de las rutas públicas. Cada email cuesta créditos y cuenta para tus límites de envío.
- Usa claves distintas para cada entorno para poder revocar una clave de vista previa sin tocar la de producción.