CheckDisposable Emailcheckdisposable.email
← All guidesFastify guide · TypeScript

Block disposable email signups in Fastify

Use a preHandler hook on your signup route. Fastify's built-in schema validation makes the email-shape check explicit; the disposable check goes right after.

The code

// src/routes/signup.ts
import { FastifyPluginAsync } from 'fastify';

async function isDisposable(email: string) {
  const r = await fetch(
    `https://api.checkdisposable.email/v1/check?email=${encodeURIComponent(email)}`,
    { headers: { Authorization: `Bearer ${process.env.CDE_KEY!}` } }
  );
  if (!r.ok) return false;
  return (await r.json()).is_disposable === true;
}

const signup: FastifyPluginAsync = async (app) => {
  app.post(
    '/signup',
    {
      schema: {
        body: {
          type: 'object',
          required: ['email', 'password'],
          properties: {
            email: { type: 'string', format: 'email' },
            password: { type: 'string', minLength: 8 },
          },
        },
      },
      preHandler: async (req, reply) => {
        const { email } = req.body as { email: string };
        if (await isDisposable(email)) {
          reply.code(400).send({ error: 'Please use a real email address.' });
        }
      },
    },
    async (req) => {
      // ...create user
      return { ok: true };
    }
  );
};

export default signup;

Notes

preHandler vs handler
Putting the check in `preHandler` means the main handler only runs for clean emails. Fastify automatically short-circuits the chain when the preHandler sends a response.
Schema first
The JSON schema rejects malformed emails before the disposable check, saving API calls for never-valid input.
Plugin pattern
Wrap the disposable check in a Fastify plugin (`fastify-plugin`) so any route can opt in by calling `app.register(disposablePlugin)` and adding `preHandler: app.blockDisposable`.

Get a free API key

500 checks/month, no credit card. No credit card. 30 seconds.

Sign up free →