@nestbolt/disposable-email
Quick Start
Go from zero to blocking disposable emails in your NestJS application in just a few steps.
This guide walks you through the complete setup, from registering the module to rejecting requests that contain disposable email addresses.
Step 1: Register the Module
Import DisposableEmailModule and register it in your root AppModule using forRoot(). The module is registered globally by default, so you only need to import it once:
import { Module } from "@nestjs/common";
import { DisposableEmailModule } from "@nestbolt/disposable-email";
@Module({
imports: [DisposableEmailModule.forRoot()],
})
export class AppModule {}Calling forRoot() with no arguments uses the default configuration: the bundled domain list is loaded, subdomain matching is disabled, and no whitelist is applied. See the Configuration page for all available options.
Step 2: Enable Dependency Injection for class-validator
The @IsNotDisposableEmail() decorator needs access to the DisposableEmailService through NestJS dependency injection. To make this work, you must call useContainer() from class-validator in your main.ts bootstrap function.
This step is required for any class-validator custom constraint that uses NestJS dependency injection:
import { NestFactory } from "@nestjs/core";
import { ValidationPipe } from "@nestjs/common";
import { useContainer } from "class-validator";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Connect class-validator to the NestJS DI container
useContainer(app.select(AppModule), { fallbackOnErrors: true });
// Enable automatic validation of incoming requests
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.listen(3000);
}
bootstrap();The fallbackOnErrors: true option ensures that if the DI container cannot resolve a validator, class-validator falls back to creating the instance itself rather than throwing an error.
Step 3: Create a DTO with the Decorator
Add the @IsNotDisposableEmail() decorator to any email property in your DTOs. It works alongside other class-validator decorators:
import { IsEmail, IsNotEmpty, MinLength } from "class-validator";
import { IsNotDisposableEmail } from "@nestbolt/disposable-email";
export class CreateUserDto {
@IsNotEmpty()
@IsEmail()
@IsNotDisposableEmail()
email: string;
@IsNotEmpty()
@MinLength(2)
name: string;
@IsNotEmpty()
@MinLength(8)
password: string;
}The decorators are evaluated in order. If the value is empty, @IsNotEmpty() fails first. If it is not a valid email format, @IsEmail() fails. Only if both of those pass does @IsNotDisposableEmail() check whether the domain is disposable.
Step 4: Use the DTO in a Controller
Create a controller that uses the DTO. When the ValidationPipe is enabled (as configured in Step 2), the DTO is automatically validated before your handler method executes:
import { Body, Controller, Post } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
@Controller("users")
export class UsersController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
// This code only runs if validation passes.
// At this point, we know the email is not disposable.
return {
message: "User created successfully",
email: createUserDto.email,
name: createUserDto.name,
};
}
}Testing It Out
Start your application:
npm run start:devSend a request with a legitimate email:
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"email": "user@gmail.com", "name": "John Doe", "password": "securepass123"}'This request succeeds with a 201 Created response.
Now send a request with a disposable email:
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"email": "user@mailinator.com", "name": "John Doe", "password": "securepass123"}'This request is rejected with a 400 Bad Request:
{
"statusCode": 400,
"message": ["Disposable email addresses are not allowed."],
"error": "Bad Request"
}Complete Application Example
Here is the full minimal application with all files in one place:
app.module.ts
import { Module } from "@nestjs/common";
import { DisposableEmailModule } from "@nestbolt/disposable-email";
import { UsersController } from "./users.controller";
@Module({
imports: [DisposableEmailModule.forRoot()],
controllers: [UsersController],
})
export class AppModule {}main.ts
import { NestFactory } from "@nestjs/core";
import { ValidationPipe } from "@nestjs/common";
import { useContainer } from "class-validator";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
useContainer(app.select(AppModule), { fallbackOnErrors: true });
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.listen(3000);
}
bootstrap();dto/create-user.dto.ts
import { IsEmail, IsNotEmpty, MinLength } from "class-validator";
import { IsNotDisposableEmail } from "@nestbolt/disposable-email";
export class CreateUserDto {
@IsNotEmpty()
@IsEmail()
@IsNotDisposableEmail()
email: string;
@IsNotEmpty()
@MinLength(2)
name: string;
@IsNotEmpty()
@MinLength(8)
password: string;
}users.controller.ts
import { Body, Controller, Post } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
@Controller("users")
export class UsersController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
return {
message: "User created successfully",
email: createUserDto.email,
name: createUserDto.name,
};
}
}What Happens Without useContainer
If you skip the useContainer() call in main.ts, the @IsNotDisposableEmail() decorator still works, but it operates in standalone mode. In this mode, the decorator cannot access the DisposableEmailService through dependency injection, so it falls back to loading the bundled domains.json file directly.
Standalone mode has the following limitations:
- The
whitelistconfiguration option has no effect. - Subdomain matching (
includeSubdomains) is not available. - Custom fetchers are not used.
- Domain updates via
updateDomains()do not affect the decorator's domain set.
For the full feature set, always configure useContainer() as shown in Step 2.
Next Steps
- Configuration -- customize the module with whitelists, subdomain matching, custom sources, and more.
- Decorator -- learn about custom error messages, validation groups, and combining with other validators.
- Service API -- use the service directly for programmatic email checks outside of DTOs.