Sending email
NestJS Mailer + Nodemailer/SMTP, Pug templates, and an authenticated REST endpoint with inline HTML.
Overview
The email module wraps @nestjs-modules/mailer with the Pug adapter. In development, use MailHog (Docker). Use sendMail with inline html or template + context; there is also POST /email/send.
Key points
- • SMTP transport via ConfigService (smtp.*)
- • EmailService exported for other modules
- • Pug templates in modules/email/templates
- • sendMail allows inline html (template adapter is skipped when html is present)
- • JWT-protected REST — no @Roles
Structure
src/modules/email/
├── email.module.ts
├── email.controller.ts
├── email.service.ts
├── dtos/
│ └── send-email.dto.ts
└── templates/
├── layout.pug
└── *.pug
src/config/smtp.config.ts
Pug templates
Add .pug files under templates/. The file name (without extension) is the template value. Use layout.pug with extends for shared HTML.
- • layout.pug — base HTML structure (optional, via extends)
- • template: 'welcome' → templates/welcome.pug
- • context — object with variables available in Pug
await this.emailService.sendMail({
to: '[email protected]',
subject: 'Welcome',
template: 'welcome',
context: { name: 'Alice' },
});
SMTP configuration
smtp namespace in src/config/smtp.config.ts. Env vars:
SMTP_HOST=localhost SMTP_PORT=587 SMTP_SECURE=false SMTP_REQUIRE_TLS=false SMTP_USER= SMTP_PASSWORD= [email protected]
Docker / MailHog
In Compose, the app points at MailHog (SMTP 1025). Open /mailhog/ to inspect messages.
EmailService
Public service methods:
- • sendMail(options) — pass-through; use html for inline or template + context
- • sendEmailConfirmation(email, token) — Auth helper (email-confirmation template)
- • sendPasswordReset(email, token) — Auth helper (password-reset template)
REST API
JWT required. Any authenticated user can call it (no @Roles).
Send generic email (inline HTML)
Body: to (email), subject, html. The controller uses sendMail with html — no template.
POST /api/v1/email/send
Content-Type: application/json
Authorization: Bearer <access_token>
{
"to": "[email protected]",
"subject": "Test Email",
"html": "Hello
This is a test email
"
}Response
{
"message": "Email sent successfully"
}
Auth integration
AuthModule imports EmailModule and uses helpers with their own templates (email-confirmation, password-reset):
- • POST /auth/register → sendEmailConfirmation
- • POST /auth/resend-verification → sendEmailConfirmation
- • POST /auth/forgot-password → sendPasswordReset
Use in another module
Import EmailModule and inject EmailService. Inline or template:
@Module({
imports: [EmailModule],
providers: [MyService],
})
export class MyModule {}
@Injectable()
export class MyService {
constructor(private readonly emailService: EmailService) {}
// Inline HTML (template adapter skipped)
async notifyInline(email: string) {
await this.emailService.sendMail({
to: email,
subject: 'Hello',
html: 'Hi
',
});
}
// Pug template
async notifyTemplate(email: string, name: string) {
await this.emailService.sendMail({
to: email,
subject: 'Welcome',
template: 'welcome',
context: { name },
});
}
}
Notes
Development
With MailHog, SMTP_SECURE and SMTP_REQUIRE_TLS should be false; auth can be empty.
Production
Use a real SMTP provider (SendGrid, SES, etc.) and strong credentials. Never commit passwords. Pug files are copied to dist via nest-cli assets.