Better Auth · TypeScript
Block disposable email in Better Auth
Better Auth fires a databaseHooks.user.create.before hook for every signup path — email/password, Google OAuth, magic link. One check covers them all.
The code
// lib/auth.ts
import { betterAuth } from "better-auth";
const PIISEND_URL = "https://api.piisend.com/api/v1/intelligence/email";
async function isDisposable(email: string) {
const r = await fetch(PIISEND_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIISEND_API_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
});
if (!r.ok) return false;
return (await r.json()).signals?.disposable === true;
}
export const auth = betterAuth({
databaseHooks: {
user: {
create: {
before: async (user) => {
if (user.email && await isDisposable(user.email)) {
throw new Error("Please use a permanent email address.");
}
return { data: user };
},
},
},
},
});API key
Keep `PIISEND_API_KEY` in server env only — Better Auth hooks run server-side.
Fail-open vs fail-closed
Wrap `isDisposable` in try/catch and return false on error if you prefer availability over strict blocking.
Pair with verification
The thrown error surfaces to your signup UI via Better Auth's normal error handling.
Send verification email with Piisend
After blocking disposable addresses, send OTP and verification mail through the same Piisend account. One API key for intelligence checks and transactional sends.