# Enviar emails con Next.js

> Envía emails desde route handlers y server actions de Next.js con el SDK de Node.js de Emailit, mantén la clave de API en el servidor y verifica los webhooks.

En esta guía se explica cómo enviar emails desde una aplicación Next.js con el SDK `@emailit/node`, tanto desde un route handler como desde una server action, y cómo verificar los webhooks de Emailit. Los ejemplos usan el App Router; el Pages Router se trata al final de la sección de envío.

## Requisitos previos

- Next.js 14 o posterior. El SDK requiere Node.js 18 o posterior.
- Un [dominio de envío verificado](/es/docs/domains/add-a-domain/), por ejemplo `acme.com`.
- Una [clave de API](/es/docs/developers/api-keys/). Basta con una clave Sending Only limitada a tu dominio.
- Hasta que tu espacio de trabajo tenga [acceso de producción](/es/docs/workspaces/production-access/), solo puedes enviar a las direcciones de email de las cuentas de los miembros del espacio de trabajo.

## Instalar el SDK

```bash
npm install @emailit/node server-only
```

`server-only` hace que la compilación falle si un componente de cliente importa el módulo que contiene tu clave.

## Configurar la clave de API

Añade la clave a `.env.local` para el desarrollo y a las variables de entorno de tu proveedor de hosting (por ejemplo, la configuración del proyecto en Vercel) para producción:

```bash title=".env.local"
EMAILIT_API_KEY=secret_••••••••••••••••••••••••••••••••
EMAILIT_WEBHOOK_SECRET=whsec_••••••••
```

> **No expongas nunca la clave al navegador:** No pongas el prefijo `NEXT_PUBLIC_` a estas variables y no llames a Emailit desde componentes de cliente. Todo lo que lleva el prefijo `NEXT_PUBLIC_` se incluye en el JavaScript que puede leer cualquier visitante.

Crea un único cliente en un módulo solo de servidor:

```typescript title="lib/emailit.ts"

```

El SDK aún no tiene declaraciones de TypeScript. Si el compilador da error, añade un archivo de declaraciones:

```typescript title="types/emailit.d.ts"
declare module '@emailit/node';
```

## Enviar desde un route handler

Un route handler es el sitio adecuado para los envíos que inicia tu propio frontend u otros servicios. Este handler de un formulario de contacto envía a una dirección interna fija, así que los visitantes no pueden usarlo para enviar emails a cualquiera:

```typescript title="app/api/contact/route.ts"

  const { email, message } = await request.json();

  if (typeof email !== 'string' || typeof message !== 'string' || !email.includes('@')) {
    return Response.json({ error: 'Invalid input' }, { status: 400 });
  }

  try {
    const sent = await emailit.emails.send({
      from: 'Acme website <website@acme.com>',
      to: 'support@acme.com',
      reply_to: email,
      subject: 'New contact form message',
      text: message,
    });
    return Response.json({ id: sent.id });
  } catch (error) {
    console.error(error);
    return Response.json({ error: 'Could not send the message' }, { status: 502 });
  }
}
```

Llámalo desde el navegador con `fetch('/api/contact', { method: 'POST', body: JSON.stringify(data) })`. La clave nunca sale del servidor.

## Enviar desde una server action

Las server actions se ejecutan en el servidor, así que pueden usar el cliente directamente:

```typescript title="app/signup/actions.ts"
'use server';

  const email = formData.get('email');
  if (typeof email !== 'string' || !email.includes('@')) return;

  // Create the account here, then send the welcome email.
  await emailit.emails.send({
    from: 'Acme <hello@acme.com>',
    to: email,
    template: 'welcome',
    variables: { email },
  });
}
```

```tsx title="app/signup/page.tsx"

  return (
    <form action={signUp}>
      <input type="email" name="email" required />
      <button type="submit">Create account</button>
    </form>
  );
}
```

`template` acepta el alias o el ID `tem_` de una plantilla; consulta [Plantillas](/es/docs/templates/). Las server actions son endpoints públicos, así que valida los datos de entrada y añade tu protección habitual contra bots y abusos antes de enviar.

### Pages Router

Con el Pages Router, envía en su lugar desde una ruta de API:

```typescript title="pages/api/contact.ts"

  if (req.method !== 'POST') return res.status(405).end();
  const sent = await emailit.emails.send({
    from: 'Acme website <website@acme.com>',
    to: 'support@acme.com',
    subject: 'New contact form message',
    text: String(req.body.message ?? ''),
  });
  res.status(200).json({ id: sent.id });
}
```

## Enviar por SMTP

Si ya usas Nodemailer, configúralo con el relay de Emailit dentro de un route handler o una server action (SMTP necesita el runtime de Node.js, no el runtime Edge):

```typescript title="lib/mailer.ts"

  host: 'smtp.emailit.com',
  port: 587,
  secure: false,
  requireTLS: true,
  auth: { user: 'emailit', pass: process.env.EMAILIT_API_KEY },
});
```

Después, llama a `await mailer.sendMail({ from, to, subject, html })`. Las funciones serverless abren una conexión SMTP nueva en la mayoría de las invocaciones, así que la API suele ser más rápida en Vercel y en plataformas similares. Consulta [Configuración SMTP](/es/docs/smtp/settings/).

## Recibir webhooks

[Crea un webhook](/es/docs/webhooks/set-up/) que apunte a `https://your-app.com/api/webhooks/emailit`. El handler debe verificar la firma con el cuerpo en bruto, así que léelo con `request.text()` antes de analizarlo:

```typescript title="app/api/webhooks/emailit/route.ts"

type EmailitEvent = {
  event_id: string;
  type: string;
  data: { object: Record<string, unknown> };
};

function isValid(rawBody: string, signature: string | null, timestamp: string | null) {
  if (!signature || !timestamp) return false;
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;
  const expected = createHmac('sha256', process.env.EMAILIT_WEBHOOK_SECRET!)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && timingSafeEqual(a, b);
}

  const rawBody = await request.text();
  const valid = isValid(
    rawBody,
    request.headers.get('x-emailit-signature'),
    request.headers.get('x-emailit-timestamp'),
  );
  if (!valid) return new Response('Invalid signature', { status: 401 });

  // Each request carries an array of up to 100 events.
  const events = JSON.parse(rawBody) as EmailitEvent[];
  for (const event of events) {
    if (event.type === 'email.bounced') {
      // Flag event.data.object.to in your database.
    }
  }

  return new Response(null, { status: 200 });
}
```

Devuelve un `2xx` en menos de 30 segundos; las demás respuestas se reintentan. Consulta [Firma de las peticiones](/es/docs/webhooks/request-signature/) y [Tipos de eventos](/es/docs/webhooks/event-types/).

## Consejos para producción

- **Haz los envíos en el servidor.** Solo `lib/emailit.ts`, los route handlers y las server actions deben acceder a la clave. `server-only` lo garantiza en tiempo de compilación.
- **No dejes nunca que el cliente elija el remitente.** Fija `from` en el código y acepta `to` desde el navegador solo cuando sea la dirección del propio usuario que ha iniciado sesión.
- **Protege los endpoints públicos.** Limita la frecuencia de los formularios de contacto y de las acciones de registro, y añade un CAPTCHA si los encuentran los bots. Cada email cuesta créditos y cuenta para tus [límites de envío](/es/docs/limits/).
- **Define las variables por entorno.** Usa claves distintas para los despliegues de vista previa y de producción para poder revocar una sin afectar a la otra.

## Próximos pasos

  - [Guía de Node.js](/es/docs/frameworks/nodejs/): La gestión de errores y Nodemailer con más detalle.
  - [Enviar emails con la API](/es/docs/email-api/send-email/): Adjuntos, programación y seguimiento.
  - [Plantillas](/es/docs/templates/): Diseña los emails una vez y envíalos por su alias.
  - [Claves de API](/es/docs/developers/api-keys/): Permisos, limitación por dominio y rotación.

---
Fuente: https://emailit.com/es/docs/frameworks/nextjs/
