# Invia email con Next.js

> Invia email dai route handler e dalle server action di Next.js con l’SDK Node.js di Emailit, tieni la chiave API sul server e verifica i webhook.

Questa guida mostra come inviare email da un’app Next.js con l’SDK `@emailit/node`, sia da un route handler sia da una server action, e come verificare i webhook di Emailit. Gli esempi usano l’App Router; il Pages Router è trattato alla fine della sezione sull’invio.

## Prerequisiti

- Next.js 14 o versioni successive. L’SDK richiede Node.js 18 o versioni successive.
- Un [dominio di invio verificato](/it/docs/domains/add-a-domain/), ad esempio `acme.com`.
- Una [chiave API](/it/docs/developers/api-keys/). Basta una chiave di solo invio limitata al tuo dominio.
- Finché il workspace non ha l’[accesso alla produzione](/it/docs/workspaces/production-access/), puoi inviare solo agli indirizzi email degli account dei membri del workspace.

## Installa l’SDK

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

`server-only` fa fallire la build se un client component importa il modulo che contiene la chiave.

## Configura la chiave API

Aggiungi la chiave a `.env.local` per lo sviluppo e alle variabili d’ambiente del tuo hosting (ad esempio, le impostazioni del progetto Vercel) per la produzione:

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

> **Non esporre mai la chiave al browser:** Non aggiungere a queste variabili il prefisso `NEXT_PUBLIC_` e non chiamare Emailit dai client component. Tutto ciò che ha il prefisso `NEXT_PUBLIC_` viene incluso nel JavaScript che ogni visitatore può leggere.

Crea un unico client in un modulo solo server:

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

```

L’SDK non ha ancora dichiarazioni TypeScript. Se il compilatore segnala errori, aggiungi un file di dichiarazione:

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

## Invia da un route handler

Un route handler è il posto giusto per gli invii avviati dal tuo frontend o da altri servizi. Questo handler per un modulo di contatto invia a un indirizzo interno fisso, così i visitatori non possono usarlo per scrivere a persone qualsiasi:

```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 });
  }
}
```

Chiamalo dal browser con `fetch('/api/contact', { method: 'POST', body: JSON.stringify(data) })`. La chiave non lascia mai il server.

## Invia da una server action

Le server action vengono eseguite sul server, quindi possono usare direttamente il client:

```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` accetta l’alias di un template o un ID `tem_`; vedi [Template](/it/docs/templates/). Le server action sono endpoint pubblici, quindi convalida l’input e aggiungi le consuete protezioni contro bot e abusi prima di inviare.

### Pages Router

Con il Pages Router, invia invece da una API route:

```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 });
}
```

## Invia con SMTP

Se usi già Nodemailer, configuralo con il relay di Emailit all’interno di un route handler o di una server action (SMTP richiede il runtime Node.js, non il 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 },
});
```

Poi chiama `await mailer.sendMail({ from, to, subject, html })`. Le funzioni serverless aprono una nuova connessione SMTP quasi a ogni invocazione, quindi su Vercel e piattaforme simili l’API di solito è più veloce. Vedi [Impostazioni SMTP](/it/docs/smtp/settings/).

## Ricevi i webhook

[Crea un webhook](/it/docs/webhooks/set-up/) che punta a `https://your-app.com/api/webhooks/emailit`. L’handler deve verificare la firma sul corpo grezzo, quindi leggilo con `request.text()` prima di analizzarlo:

```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 });
}
```

Restituisci un `2xx` entro 30 secondi; le altre risposte vengono ritentate. Vedi [Firma delle richieste](/it/docs/webhooks/request-signature/) e [Tipi di evento](/it/docs/webhooks/event-types/).

## Consigli per la produzione

- **Tieni gli invii sul server.** Solo `lib/emailit.ts`, i route handler e le server action devono accedere alla chiave. `server-only` lo impone in fase di build.
- **Non lasciare mai che il client scelga il mittente.** Scrivi `from` direttamente nel codice e accetta `to` dal browser solo quando è l’indirizzo dell’utente che ha effettuato l’accesso.
- **Proteggi gli endpoint pubblici.** Limita la frequenza dei moduli di contatto e delle azioni di registrazione, e aggiungi un CAPTCHA se i bot li trovano. Ogni email costa crediti e rientra nei [limiti di invio](/it/docs/limits/).
- **Imposta le variabili per ambiente.** Usa chiavi separate per i deployment di anteprima e di produzione, così puoi revocarne una senza effetti sull’altra.

## Passaggi successivi

  - [Guida per Node.js](/it/docs/frameworks/nodejs/): Gestione degli errori e Nodemailer più nel dettaglio.
  - [Invia email con l’API](/it/docs/email-api/send-email/): Allegati, programmazione e tracciamento.
  - [Template](/it/docs/templates/): Progetta le email una volta e inviale per alias.
  - [Chiavi API](/it/docs/developers/api-keys/): Permessi, limitazioni a un dominio e rotazione.

---
Fonte: https://emailit.com/it/docs/frameworks/nextjs/
