# Odesílání e-mailů z Next.js

> Odesílejte e-maily z route handlerů a server actions v Next.js přes Node.js SDK Emailitu, držte API klíč na serveru a ověřujte webhooky.

Tento návod ukazuje, jak odesílat e-maily z aplikace v Next.js přes SDK `@emailit/node`, a to z route handleru i ze server action, a jak ověřovat webhooky Emailitu. Příklady používají App Router; Pages Router popisuje konec části o odesílání.

## Předpoklady

- Next.js 14 nebo novější. SDK potřebuje Node.js 18 nebo novější.
- [Ověřená odesílací doména](/cs/docs/domains/add-a-domain/), například `acme.com`.
- [API klíč](/cs/docs/developers/api-keys/). Stačí klíč jen pro odesílání omezený na vaši doménu.
- Dokud váš workspace nemá [produkční přístup](/cs/docs/workspaces/production-access/), můžete odesílat jen na e-mailové adresy účtů členů workspace.

## Nainstalujte SDK

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

Díky `server-only` sestavení selže, pokud modul s vaším klíčem importuje klientská komponenta.

## Nastavte API klíč

Pro vývoj přidejte klíč do `.env.local` a pro produkci do proměnných prostředí u svého hostingu (například v nastavení projektu ve Vercelu):

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

> **Nikdy klíč nezpřístupňujte prohlížeči:** Nedávejte těmto proměnným předponu `NEXT_PUBLIC_` a nevolejte Emailit z klientských komponent. Vše s předponou `NEXT_PUBLIC_` se přibalí do JavaScriptu, který si může přečíst každý návštěvník.

Vytvořte jednoho klienta v modulu, který běží jen na serveru:

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

```

SDK zatím nemá deklarace typů pro TypeScript. Pokud si kompilátor stěžuje, přidejte soubor s deklarací:

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

## Odesílejte z route handleru

Route handler je správné místo pro odesílání, které spouští váš vlastní frontend nebo jiné služby. Tento handler kontaktního formuláře odesílá na pevnou interní adresu, takže ho návštěvníci nemohou zneužít k posílání e-mailů komukoli:

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

Z prohlížeče ho volejte přes `fetch('/api/contact', { method: 'POST', body: JSON.stringify(data) })`. Klíč nikdy neopustí server.

## Odesílejte ze server action

Server actions běží na serveru, takže mohou klienta používat přímo:

```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` přijímá alias šablony nebo ID `tem_`; viz [Šablony](/cs/docs/templates/). Server actions jsou veřejné endpointy, proto před odesláním validujte vstup a přidejte svou obvyklou ochranu proti botům a zneužití.

### Pages Router

S Pages Routerem odesílejte místo toho z 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 });
}
```

## Odesílání přes SMTP

Pokud už používáte Nodemailer, nastavte ho na SMTP relay Emailitu uvnitř route handleru nebo server action (SMTP potřebuje běhové prostředí Node.js, ne 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 },
});
```

Pak zavolejte `await mailer.sendMail({ from, to, subject, html })`. Serverless funkce při většině volání otevírají nové SMTP spojení, takže na Vercelu a podobných platformách je API obvykle rychlejší. Viz [Nastavení SMTP](/cs/docs/smtp/settings/).

## Přijímejte webhooky

[Vytvořte webhook](/cs/docs/webhooks/set-up/), který míří na `https://your-app.com/api/webhooks/emailit`. Handler musí ověřit podpis vůči surovému tělu požadavku, proto ho před parsováním načtěte pomocí `request.text()`:

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

Do 30 sekund vraťte `2xx`; při jiných odpovědích se požadavek opakuje. Viz [Ověření podpisu webhooků](/cs/docs/webhooks/request-signature/) a [Typy událostí](/cs/docs/webhooks/event-types/).

## Tipy pro produkční provoz

- **Odesílejte jen ze serveru.** S klíčem by měly pracovat jen `lib/emailit.ts`, route handlery a server actions. `server-only` to vynucuje už při sestavení.
- **Nikdy nenechte klienta vybrat odesílatele.** Adresu `from` zadejte napevno a `to` z prohlížeče přijímejte, jen když jde o vlastní adresu přihlášeného uživatele.
- **Chraňte veřejné endpointy.** U kontaktních formulářů a registračních akcí omezte rychlost, a pokud je najdou boti, přidejte CAPTCHA. Každý e-mail stojí kredity a počítá se do vašich [limitů odesílání](/cs/docs/limits/).
- **Nastavte proměnné pro každé prostředí zvlášť.** Pro náhledová a produkční nasazení používejte samostatné klíče, abyste mohli jeden odvolat, aniž by to ovlivnilo druhý.

## Další kroky

  - [Návod pro Node.js](/cs/docs/frameworks/nodejs/): Ošetření chyb a Nodemailer podrobněji.
  - [Odesílání e-mailů přes API](/cs/docs/email-api/send-email/): Přílohy, plánování a měření.
  - [Šablony](/cs/docs/templates/): Navrhněte e-maily jednou a odesílejte je podle aliasu.
  - [API klíče](/cs/docs/developers/api-keys/): Rozsahy oprávnění, omezení na doménu a výměna.

---
Zdroj: https://emailit.com/cs/docs/frameworks/nextjs/
