Better Auth und Resend: Authentifizierung, die ein Solo-Entwickler wirklich ausliefern kann
- auth
- better-auth
- resend
Self-hosted Auth mit Better Auth, transaktionale E-Mails mit Resend. Kein Vendor Lock-in, keine versteckten Kosten, und ein Setup, das ein KI-Coding-Agent aus einer Markdown-Datei reproduzieren kann.
Authentifizierung ist der Teil jedes Projekts, der einfach wirkt, bis man drei Tage später um Mitternacht OAuth-Redirect-Loops debuggt. Meine Anforderung für NearYou war klar: E-Mail- und Passwort-Anmeldung, Google Social Login, E-Mail-Verifizierung, Passwort-Reset, Zwei-Faktor später, und null monatliche Kosten für einen Dienst, der in der frühen Entwicklung wochenlang inaktiv sein könnte. Better Auth plus Resend ist dieser Stack. Ich zeige dir genau, wie er zusammenpasst.
Warum Better Auth
Better Auth ist eine self-hosted Authentifizierungsbibliothek für das JavaScript-Ökosystem. Du installierst es als Paket, schließt es über einen Adapter an deine bestehende Datenbank an und besitzt die gesamte Session- und Benutzertabelle. Kein externer Authentifizierungsdienst ruft nach Hause. Kein Preis pro monatlich aktivem Nutzer, der dich überrascht, wenn ein Beitrag viral geht. Keine Anbietermigration, wenn sich die Preise ändern.
Der Funktionsumfang ist produktionsbereit von Anfang an: E-Mail und Passwort, Social Provider (Google, Facebook, GitHub und mehr), E-Mail-Verifizierung, Passwort-Reset, Zwei-Faktor-Authentifizierung über TOTP und ein Prisma-Adapter, der die notwendigen Schema-Modelle automatisch generiert. Sessions werden in deiner eigenen Datenbank gespeichert. Token-Rotation wird verwaltet. Die Client-Bibliothek liefert typisierte Hooks für React und andere Frameworks, sodass du keine eigene useSession-Logik schreiben musst.
Das Einzige, was Better Auth nicht enthält, ist die E-Mail-Zustellung. Es stellt Hooks bereit, du verbindest den Transport. Das ist die richtige Trennung: Auth-Logik bleibt in Better Auth, Zustellung geht an einen Dienst, der genau dafür gebaut ist.

Warum Resend für transaktionale E-Mails
Resend ist ein entwicklerorientierter transaktionaler E-Mail-Dienst. Das kostenlose Tier bietet 3.000 E-Mails pro Monat und 100 pro Tag, was jedes Frühphasenprojekt komfortabel abdeckt. Die kostenpflichtigen Preise sind niedrig genug, dass man sich keine Gedanken machen muss, bis man eine echte Nutzerbasis hat. Die API ist ein einziger Funktionsaufruf. Keine SMTP-Konfiguration, kein Alptraum mit Authentifizierungs-Headern, nirgendwo XML.
Die Anforderungen sind minimal, aber nicht verhandelbar. Du brauchst eine Sendedomain, die du kontrollierst. Du musst drei DNS-Einträge zu dieser Domain hinzufügen: einen SPF-Eintrag, einen DKIM-Schlüssel, den Resend für dich generiert, und eine DMARC-Richtlinie. Du brauchst einen API-Schlüssel vom Resend-Dashboard. Und du brauchst eine klare, konsistente from-Adresse, die zu deiner verifizierten Domain passt. Sobald diese vorhanden sind, funktioniert die Zustellung und die Inbox-Platzierung ist solide.
Den Resend-Aufruf verdrahten
Das Resend Node SDK ist ein kleiner Wrapper um ihre REST API. Installiere es mit npm install resend, exportiere eine typisierte Funktion pro E-Mail-Typ und rufe sie aus deinen Better Auth Hooks auf. Die wichtigste Disziplin: Baue niemals HTML-Strings inline in der Auth-Konfiguration. Verlagere das in ein dediziertes lib/email.ts-Modul, damit es testbar und austauschbar ist.
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendVerificationEmail(to: string, url: string) {
await resend.emails.send({
from: 'NearYou <noreply@mail.nearyou.app>',
to,
subject: 'Verify your email address',
html:
'<p>Click the link below to verify your account:</p>' +
'<p><a href="' + url + '">' + url + '</a></p>',
});
}Halte RESEND_API_KEY in deiner .env-Datei und committe sie niemals. Die from-Adresse muss exakt mit deiner verifizierten Domain übereinstimmen. Wenn du die Sendedomain später änderst, aktualisiere diesen String und verifiziere erneut im Resend-Dashboard. Die Funktion ist bewusst einfach: ein Send-Aufruf, eine Fehleroberfläche. Füge vor dem Produktionsgang ordentliche Fehlerbehandlung und Logging hinzu.
Better Auth damit verbinden
Die Better Auth Konfiguration lebt in einer einzigen auth.ts-Datei im Projektstamm oder in lib/. Der emailVerification-Block ist der Ort, an dem du deine Send-Funktion übergibst. Der Hook empfängt das User-Objekt und eine vorgefertigte Verifizierungs-URL. Du leitest beides an deinen Resend-Wrapper weiter. Die URL ist intern von Better Auth signiert und zeitlich begrenzt, sodass du selbst keine Tokens verwaltest.
import { betterAuth } from 'better-auth';
import { prismaAdapter } from 'better-auth/adapters/prisma';
import { prisma } from './lib/prisma';
import { sendVerificationEmail } from './lib/email';
export const auth = betterAuth({
database: prismaAdapter(prisma, { provider: 'mysql' }),
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
},
},
emailVerification: {
sendOnSignUp: true,
autoSignInAfterVerification: true,
sendVerificationEmail: async ({ user, url }) => {
await sendVerificationEmail(user.email, url);
},
},
});Das Flag requireEmailVerification: true blockiert die Anmeldung, bis die E-Mail bestätigt ist. autoSignInAfterVerification: true bedeutet, dass der Nutzer sofort nach dem Klick auf den Link in seinem Posteingang in der App landet, kein zweiter Anmeldeschritt. Diese Kombination gibt dir einen sauberen Onboarding-Flow mit minimalem Code.
- Passwort-Reset: füge
sendResetPasswordzumemailAndPassword-Block hinzu, gleiches Muster wie der Verifizierungs-Hook. - Social Provider: setze
google.clientIdundgoogle.clientSecretaus der Umgebung. Better Auth verwaltet die OAuth-Callback-Route automatisch. - Zwei-Faktor: füge
import { twoFactor } from "better-auth/plugins"hinzu und nimm es in dasplugins-Array auf. Better Auth liefert die TOTP-Logik. - Session-Ablauf: setze
session.expiresInundsession.updateAge, um zu steuern, wie lange Tokens leben und wann sie sich automatisch aktualisieren.
DNS-Anforderungen: der Teil, der dich blockiert
E-Mail-Zustellbarkeit steht und fällt mit DNS. Resend sendet nicht von einer nicht verifizierten Domain. Selbst wenn du diese Prüfung irgendwie umgehst, landen E-Mails ohne SPF- und DKIM-Ausrichtung im Spam. Das ist der einzige Schritt mit einer echten Wartezeit: DNS-Änderungen können bis zu 48 Stunden benötigen, um sich global zu verbreiten, in der Praxis ist es mit modernen Registraren jedoch meist unter einer Stunde.
Die drei Einträge, die du auf deiner Sendedomain benötigst: SPF teilt empfangenden Servern mit, welche Hosts für deine Domain senden dürfen. DKIM hängt eine kryptografische Signatur an ausgehende Nachrichten an, damit Empfänger überprüfen können, dass sie nicht manipuliert wurden. DMARC teilt empfangenden Servern mit, was zu tun ist, wenn SPF- oder DKIM-Prüfungen fehlschlagen. Resend zeigt dir die genauen Werte, die du nach dem Hinzufügen der Domain in deinen DNS-Anbieter einfügen musst.

Konfiguration in einer Markdown-Gedächtnisdatei persistieren
Ich verwende Claude Code als KI-Coding-Agent bei NearYou. Der Agent schreibt Auth-Konfiguration, verdrahtet E-Mail-Hooks, aktualisiert Umgebungsdateien und debuggt Zustellungsprobleme. Damit das zuverlässig über Sitzungen hinweg funktioniert, benötigt der Agent persistentes Gedächtnis der genauen E-Mail-Einrichtung: welche Domain verifiziert ist, was die From-Adresse ist, welche DNS-Einträge vorhanden sind und wie die Umgebungsvariablennamen lauten.
Ohne eine Gedächtnisdatei muss der Agent bei jeder neuen Sitzung fragen, ableiten oder raten. Er könnte die falsche From-Adresse verwenden, auf eine nicht verifizierte Domain verweisen oder den Hook an eine Umgebungsvariable mit einem anderen Namen verdrahten, als das, was tatsächlich in .env steht. Das sind kleine Fehler, die echte Debugging-Zeit kosten.
Die Lösung ist ein kurzer, präziser Abschnitt im AGENTS.md des Projekts (oder eine dedizierte .claude/email.md, wenn das Projekt groß ist). Halte die Sendedomain, die From-Adresse, die DNS-Eintrags-Checkliste und die Hook-Verdrahtungsnotizen dort. Der Agent liest diese Datei beim Sitzungsstart und bedient die E-Mail-Einrichtung beim ersten Versuch korrekt, jedes Mal.
## Auth + Email (Better Auth + Resend)
Sending domain: mail.nearyou.app
From address: noreply@mail.nearyou.app
Resend API key: env var RESEND_API_KEY
### DNS records required on mail.nearyou.app
- SPF: TXT "v=spf1 include:amazonses.com ~all"
- DKIM: TXT resend._domainkey <value from Resend dashboard>
- DMARC: TXT _dmarc "v=DMARC1; p=quarantine; rua=mailto:dmarc@nearyou.app"
### Email hooks wired in auth.ts
- emailVerification.sendVerificationEmail -> sendVerificationEmail()
- emailAndPassword.sendResetPassword -> sendPasswordResetEmail()
### Gotchas
- Resend requires the from domain to be verified before any email sends.
- DNS propagation can take up to 48h; verify in the Resend dashboard.
- Always set autoSignInAfterVerification: true so the UX is not broken post-verify.
- Never hardcode the from address; keep it in this file so the agent reads it.Diese Datei ist der Unterschied zwischen einem Agenten, der deinen E-Mail-Stack sicher konfiguriert, und einem, der dir drei Fragen stellt, bevor er eine einzige Zeile schreibt. Es ist keine Dokumentation für Menschen. Es ist ein kompaktes Kontextdokument für ein System, das dein Resend-Dashboard, deine DNS-Einträge oder deine .env-Datei nicht sehen kann. Jede Zeile darin verdient ihren Platz, indem sie einen echten Fehler verhindert.
Der vollständige Stack in Produktion
Bei NearYou sind Better Auth und Resend seit den ersten Beta-Nutzern in Produktion. Der Auth-Flow: Registrierung mit E-Mail und Passwort oder Google, Erhalt einer Verifizierungs-E-Mail, die innerhalb weniger Sekunden über Resend gesendet wird, Klick auf den Link, Landen in der App. Der Passwort-Reset verwendet denselben Resend-Wrapper. Zwei-Faktor über TOTP ist verdrahtet, aber für Nutzer optional. Die gesamte Auth-Oberfläche ist self-hosted, keine Anbieterabhängigkeit außer Resend für die Zustellung.
Der Betriebskostenanteil ist vernachlässigbar. Das Resend Free Tier verwaltet die aktuelle Nutzerbasis mit Spielraum. Wenn es über das kostenlose Limit wächst, sind die Kosten pro E-Mail niedrig genug, um eine Rundungsfehler im Infrastrukturbudget zu bleiben. Better Auth hat überhaupt keine Kosten pro Nutzer. Vergleiche das mit Auth0- oder Clerk-Preisen im großen Maßstab, und die Rechnung liegt nicht einmal nah.
Das beste Auth-Setup ist das, das du besitzt, verstehst und um 2 Uhr nachts debuggen kannst, ohne ein Anbieter-Support-Ticket zu öffnen.
Wenn du ein neues JavaScript- oder TypeScript-Projekt baust und Authentifizierung brauchst, fange hier an. Installiere Better Auth, schließe es an deine bestehende Prisma-Datenbank an, füge den Resend-Wrapper hinzu, setze die DNS-Einträge und fülle dein AGENTS.md aus. Du wirst in einem halben Tag ein funktionierendes, produktionsreifes Auth-System haben und das nächste Jahr keinen Gedanken daran verschwenden.