Block disposable email signups in SvelteKit
Add the check inside a `+page.server.ts` action. SvelteKit forms post directly to actions, so this is the natural place — no separate API route needed.
The code
// src/routes/signup/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import { CDE_KEY } from '$env/static/private';
import type { Actions } from './$types';
async function isDisposable(email: string) {
const r = await fetch(
`https://api.checkdisposable.email/v1/check?email=${encodeURIComponent(email)}`,
{ headers: { Authorization: `Bearer ${CDE_KEY}` } }
);
if (!r.ok) return false;
return (await r.json()).is_disposable === true;
}
export const actions: Actions = {
default: async ({ request }) => {
const form = await request.formData();
const email = String(form.get('email') ?? '');
if (await isDisposable(email)) {
return fail(400, { email, error: 'Please use a real email address.' });
}
// ...create user, set session cookie
throw redirect(303, '/dashboard');
},
};Notes
- Why $env/static/private
- SvelteKit splits env vars into public (`PUBLIC_*`) and private. Importing from `$env/static/private` makes it impossible to accidentally ship the key to the client.
- Form errors with use:enhance
- In `+page.svelte`, use `<form method="post" use:enhance>` and read `form?.error` from `$page` to render the message inline without a full page reload.
- Pair with Lucia / Auth.js
- If you use Lucia or Auth.js (sveltekit-auth), put the disposable check in the same action before calling their createUser — both libraries are agnostic to validation order.
Get a free API key
500 checks/month, no credit card. No credit card. 30 seconds.
Sign up free →