Zum Inhalt springen
Doku

Tutorial

E-Mails mit Next.js senden

Senden Sie E-Mails aus Route Handlers und Server Actions von Next.js mit dem Node.js-SDK von Emailit, halten Sie den API-Schlüssel auf dem Server und verifizieren Sie Webhooks.

Aktualisiert am 1. Okt. 2026

Diese Anleitung zeigt, wie Sie E-Mails aus einer Next.js-App mit dem SDK @emailit/node senden, sowohl aus einem Route Handler als auch aus einer Server Action, und wie Sie Emailit-Webhooks verifizieren. Die Beispiele verwenden den App Router; der Pages Router folgt am Ende des Abschnitts zum Senden.

Voraussetzungen

  • Next.js 14 oder neuer. Das SDK erfordert Node.js 18 oder neuer.
  • Eine verifizierte Versanddomain, zum Beispiel acme.com.
  • Ein API-Schlüssel. Ein reiner Sende-Schlüssel, der auf Ihre Domain beschränkt ist, genügt.
  • Solange Ihr Workspace keinen Produktionszugang hat, können Sie nur an die Konto-E-Mail-Adressen von Workspace-Mitgliedern senden.

SDK installieren

Terminal
npm install @emailit/node server-only

server-only lässt den Build fehlschlagen, wenn eine Client-Komponente das Modul importiert, das Ihren Schlüssel enthält.

API-Schlüssel konfigurieren

Legen Sie den Schlüssel für die Entwicklung in .env.local und für die Produktion in den Umgebungsvariablen Ihres Hosters ab (zum Beispiel in den Projekteinstellungen von Vercel):

.env.local
EMAILIT_API_KEY=secret_••••••••••••••••••••••••••••••••
EMAILIT_WEBHOOK_SECRET=whsec_••••••••

Erstellen Sie einen Client in einem reinen Server-Modul:

lib/emailit.ts
import 'server-only';
import { Emailit } from '@emailit/node';

export const emailit = new Emailit(process.env.EMAILIT_API_KEY!);

Das SDK hat noch keine TypeScript-Deklarationen. Wenn der Compiler sich beschwert, fügen Sie eine Deklarationsdatei hinzu:

types/emailit.d.ts
declare module '@emailit/node';

Aus einem Route Handler senden

Ein Route Handler ist der richtige Ort für Versände, die Ihr eigenes Frontend oder andere Dienste auslösen. Dieser Handler für ein Kontaktformular sendet an eine feste interne Adresse, sodass Besucher ihn nicht nutzen können, um beliebigen Personen E-Mails zu schicken:

app/api/contact/route.ts
import { emailit } from '@/lib/emailit';

export async function POST(request: Request) {
  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 });
  }
}

Rufen Sie ihn im Browser mit fetch('/api/contact', { method: 'POST', body: JSON.stringify(data) }) auf. Der Schlüssel verlässt nie den Server.

Aus einer Server Action senden

Server Actions laufen auf dem Server und können den Client daher direkt verwenden:

app/signup/actions.ts
'use server';

import { emailit } from '@/lib/emailit';

export async function signUp(formData: FormData) {
  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 },
  });
}
app/signup/page.tsx
import { signUp } from './actions';

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

template akzeptiert einen Vorlagen-Alias oder eine tem_-ID; siehe Vorlagen. Server Actions sind öffentliche Endpunkte; validieren Sie deshalb die Eingaben und ergänzen Sie vor dem Senden Ihren üblichen Schutz vor Bots und Missbrauch.

Pages Router

Mit dem Pages Router senden Sie stattdessen aus einer API-Route:

pages/api/contact.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { emailit } from '@/lib/emailit';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  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 });
}

Alternativ per SMTP senden

Wenn Sie bereits Nodemailer verwenden, konfigurieren Sie es in einem Route Handler oder einer Server Action mit dem Emailit-Relay (SMTP erfordert die Node.js-Runtime, nicht die Edge-Runtime):

lib/mailer.ts
import 'server-only';
import nodemailer from 'nodemailer';

export const mailer = nodemailer.createTransport({
  host: 'smtp.emailit.com',
  port: 587,
  secure: false,
  requireTLS: true,
  auth: { user: 'emailit', pass: process.env.EMAILIT_API_KEY },
});

Rufen Sie dann await mailer.sendMail({ from, to, subject, html }) auf. Serverless-Funktionen öffnen bei den meisten Aufrufen eine neue SMTP-Verbindung; auf Vercel und ähnlichen Plattformen ist die API daher meist schneller. Siehe SMTP-Einstellungen.

Webhooks empfangen

Erstellen Sie einen Webhook, der auf https://your-app.com/api/webhooks/emailit zeigt. Der Handler muss die Signatur anhand des unveränderten Bodys verifizieren; lesen Sie ihn deshalb vor dem Parsen mit request.text():

app/api/webhooks/emailit/route.ts
import { createHmac, timingSafeEqual } from 'node:crypto';

export const runtime = 'nodejs';

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

export async function POST(request: Request) {
  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 });
}

Geben Sie innerhalb von 30 Sekunden einen 2xx-Status zurück; bei anderen Antworten wird die Anfrage wiederholt. Siehe Anfragesignatur und Event-Typen.

Tipps für den Produktivbetrieb

  • Versand auf dem Server halten. Nur lib/emailit.ts, Route Handlers und Server Actions sollten mit dem Schlüssel arbeiten. server-only erzwingt das beim Build.
  • Den Client nie den Absender wählen lassen. Hinterlegen Sie from fest im Code und übernehmen Sie to nur dann aus dem Browser, wenn es die eigene Adresse des angemeldeten Nutzers ist.
  • Öffentliche Endpunkte schützen. Begrenzen Sie die Anfragerate von Kontaktformularen und Registrierungsaktionen und ergänzen Sie ein CAPTCHA, wenn Bots sie finden. Jede E-Mail kostet Credits und zählt gegen Ihre Versandlimits.
  • Variablen pro Umgebung setzen. Verwenden Sie getrennte Schlüssel für Preview- und Produktions-Deployments, damit Sie einen widerrufen können, ohne den anderen zu beeinträchtigen.

Nächste Schritte

Fehlerbehandlung und Nodemailer im Detail.
Anhänge, Planung und Tracking.
E-Mails einmal gestalten und per Alias senden.
Scopes, Domain-Beschränkungen und Rotation.

War diese Seite hilfreich?

Danke für Ihr Feedback.

Danke, wir lesen jede Nachricht.