Email System
Express Base API includes a complete email system with beautiful HTML templates, queue support for reliable delivery, and easy customization.
Featuresβ
- π§ Template Engine - Handlebars-based HTML templates
- π¨ Beautiful Design - Pre-built responsive email templates
- π¬ Queue Support - Background processing with Bull
- π Retry Mechanism - Automatic retry on failure
- π Attachments - Support for file attachments
- π Multi-language - Easy localization support
- β‘ Fast Delivery - Non-blocking async sending
- π οΈ Easy Testing - MailDev integration for development
Architectureβ
βββββββββββββββ
β Trigger β (Login, Register, Reset Password, etc.)
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββ
β Queue Email β Add to Bull queue (if enabled)
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββ
β Compile β Handlebars template + data
β Template β
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββ
β Send β SMTP transport
β Email β
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββ
β Delivered β β
Success or β Retry
βββββββββββββββ
Quick Startβ
Send an Emailβ
import mailService from './utils/bases/mail.service';
// Send email with template
await mailService.sendMail({
to: 'user@example.com',
subject: 'Welcome!',
template: 'activation',
context: {
user_name: 'John Doe',
activation_link: 'https://app.com/activate?token=abc123',
},
});
Using Queue (Recommended)β
import mailService from './utils/bases/mail.service';
// Add email to queue (non-blocking)
await mailService.queueMail({
to: 'user@example.com',
subject: 'Welcome!',
template: 'activation',
context: {
user_name: 'John Doe',
activation_link: 'https://app.com/activate?token=abc123',
},
});
Available Templatesβ
1. Activation Emailβ
Template: templates/auth/activation.hbs
Usage:
await mailService.sendMail({
to: user.email,
subject: 'Activate Your Account',
template: 'activation',
context: {
user_name: `${user.first_name} ${user.last_name}`,
activation_code: otp,
app_name: 'Express Base API',
},
});
Context Variables:
user_name- User's full nameactivation_code- 6-digit OTPapp_name- Application name
2. Login Notificationβ
Template: templates/auth/login.hbs
Usage:
await mailService.sendMail({
to: user.email,
subject: 'New Login Detected',
template: 'login',
context: {
user_name: `${user.first_name} ${user.last_name}`,
login_time: new Date().toLocaleString(),
device: req.device.type,
browser: req.device.browser,
location: req.location?.city || 'Unknown',
ip_address: req.ip,
security_link: `${FRONTEND_URL}/security`,
},
});
Context Variables:
user_name- User's full namelogin_time- Login timestampdevice- Device type (Desktop, Mobile, Tablet)browser- Browser namelocation- City/locationip_address- IP addresssecurity_link- Link to report suspicious activity
3. Forgot Passwordβ
Template: templates/auth/forgot-password.hbs
Usage:
await mailService.sendMail({
to: user.email,
subject: 'Reset Your Password',
template: 'forgot-password',
context: {
user_name: `${user.first_name} ${user.last_name}`,
reset_link: `${FRONTEND_URL}/reset-password?token=${resetToken}`,
expiry_time: '15 minutes',
},
});
Context Variables:
user_name- User's full namereset_link- Password reset link with tokenexpiry_time- Token validity period
4. Register OTPβ
Template: templates/auth/register-otp.hbs
Usage:
await mailService.sendMail({
to: user.email,
subject: 'Verify Your Email',
template: 'register-otp',
context: {
user_name: `${user.first_name} ${user.last_name}`,
otp_code: otp,
expiry_time: '10 minutes',
},
});
Context Variables:
user_name- User's full nameotp_code- 6-digit OTPexpiry_time- OTP validity period
5. Security Alertβ
Template: templates/auth/security-alert.hbs
Usage:
await mailService.sendMail({
to: user.email,
subject: 'Security Alert - Password Reset Required',
template: 'security-alert',
context: {
user_name: `${user.first_name} ${user.last_name}`,
alert_reason: 'Suspicious login activity detected',
reset_link: `${FRONTEND_URL}/reset-password?token=${resetToken}`,
support_email: 'support@example.com',
},
});
Context Variables:
user_name- User's full namealert_reason- Why alert was triggeredreset_link- Password reset linksupport_email- Support contact
Creating Custom Templatesβ
Step 1: Create Template Fileβ
Create a new .hbs file in templates/ directory:
<!-- templates/custom/welcome.hbs -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to {{app_name}}</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
border-radius: 10px 10px 0 0;
}
.content {
background: #f9f9f9;
padding: 30px;
border-radius: 0 0 10px 10px;
}
.button {
display: inline-block;
padding: 12px 30px;
background: #667eea;
color: white;
text-decoration: none;
border-radius: 5px;
margin: 20px 0;
}
.footer {
text-align: center;
margin-top: 30px;
color: #666;
font-size: 12px;
}
</style>
</head>
<body>
<div class="header">
<h1>Welcome to {{app_name}}!</h1>
</div>
<div class="content">
<h2>Hi {{user_name}},</h2>
<p>We're excited to have you on board! Get started by exploring these features:</p>
<ul>
{{#each features}}
<li>{{this}}</li>
{{/each}}
</ul>
<a href="{{dashboard_link}}" class="button">Go to Dashboard</a>
<p>If you have any questions, feel free to reach out to our support team.</p>
</div>
<div class="footer">
<p>© 2024 {{app_name}}. All rights reserved.</p>
<p>You're receiving this email because you registered at {{app_name}}.</p>
</div>
</body>
</html>
Step 2: Send Email with Custom Templateβ
await mailService.sendMail({
to: user.email,
subject: 'Welcome to Our Platform!',
template: 'custom/welcome',
context: {
app_name: 'Express Base API',
user_name: 'John Doe',
features: [
'Complete Authentication System',
'Redis Caching',
'File Upload',
'Email Templates',
],
dashboard_link: 'https://app.com/dashboard',
},
});
Email Service Methodsβ
sendMail()β
Send email immediately (blocking).
interface SendMailOptions {
to: string | string[];
subject: string;
template: string;
context?: Record<string, any>;
attachments?: Attachment[];
from?: string;
cc?: string | string[];
bcc?: string | string[];
}
await mailService.sendMail(options);
Example:
await mailService.sendMail({
to: ['user1@example.com', 'user2@example.com'],
cc: 'manager@example.com',
subject: 'Monthly Report',
template: 'reports/monthly',
context: {
month: 'January',
revenue: '$50,000',
growth: '15%',
},
attachments: [
{
filename: 'report.pdf',
path: './reports/january.pdf',
},
],
});
queueMail()β
Add email to queue (non-blocking, recommended).
await mailService.queueMail(options);
Example:
await mailService.queueMail({
to: user.email,
subject: 'Welcome!',
template: 'activation',
context: {
user_name: user.name,
activation_code: otp,
},
});
Benefits:
- Non-blocking - returns immediately
- Automatic retry on failure
- Survives server restarts (Redis persistence)
- Better performance for bulk emails
With Attachmentsβ
File Attachmentsβ
await mailService.sendMail({
to: user.email,
subject: 'Your Invoice',
template: 'invoice',
context: {
user_name: user.name,
invoice_number: 'INV-001',
total: '$99.99',
},
attachments: [
{
filename: 'invoice.pdf',
path: './invoices/INV-001.pdf',
},
{
filename: 'receipt.pdf',
path: './receipts/REC-001.pdf',
},
],
});
Buffer Attachmentsβ
const pdfBuffer = await generatePDF();
await mailService.sendMail({
to: user.email,
subject: 'Your Report',
template: 'report',
context: { user_name: user.name },
attachments: [
{
filename: 'report.pdf',
content: pdfBuffer,
contentType: 'application/pdf',
},
],
});
Inline Imagesβ
await mailService.sendMail({
to: user.email,
subject: 'Newsletter',
template: 'newsletter',
context: { user_name: user.name },
attachments: [
{
filename: 'logo.png',
path: './assets/logo.png',
cid: 'logo@company.com', // Use in template: <img src="cid:logo@company.com">
},
],
});
Configurationβ
Environment Variablesβ
# SMTP Configuration
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
SMTP_SECURE=false
SMTP_USER=your-smtp-user
SMTP_PASS=your-smtp-password
SMTP_FROM=noreply@example.com
SMTP_FROM_NAME="Express Base API"
# Feature Flags
USE_MAIL=yes
USE_QUEUE=yes
# Application
APP_NAME="Express Base API"
FRONTEND_URL=http://localhost:5173
SMTP Providersβ
Gmailβ
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password # Use App Password, not regular password
SendGridβ
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=apikey
SMTP_PASS=your-sendgrid-api-key
AWS SESβ
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-ses-smtp-username
SMTP_PASS=your-ses-smtp-password
Mailgunβ
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=postmaster@your-domain.mailgun.org
SMTP_PASS=your-mailgun-password
Development Testingβ
MailDevβ
Use MailDev to test emails locally without sending real emails:
# Start MailDev
npm run start:maildev
# Access web UI at http://localhost:1080
Configuration:
# .env.development
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE=false
SMTP_USER=
SMTP_PASS=
All emails will be captured by MailDev and visible in the web UI.
Mailtrapβ
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USER=your-mailtrap-user
SMTP_PASS=your-mailtrap-password
Queue Managementβ
View Queue Statusβ
# Install Bull Board for queue monitoring
npm install @bull-board/express
Add to your Express app:
import { createBullBoard } from '@bull-board/api';
import { BullAdapter } from '@bull-board/api/bullAdapter';
import { ExpressAdapter } from '@bull-board/express';
import mailQueue from './utils/bases/queues/queue.service';
const serverAdapter = new ExpressAdapter();
createBullBoard({
queues: [new BullAdapter(mailQueue)],
serverAdapter: serverAdapter,
});
app.use('/admin/queues', serverAdapter.getRouter());
Access at: http://localhost:3000/admin/queues
Retry Failed Jobsβ
import mailQueue from './utils/bases/queues/queue.service';
// Retry all failed jobs
const failed = await mailQueue.getFailed();
for (const job of failed) {
await job.retry();
}
Handlebars Helpersβ
Custom helpers available in templates:
formatDateβ
{{formatDate created_at "MMMM DD, YYYY"}}
<!-- Output: January 15, 2024 -->
uppercaseβ
{{uppercase user_name}}
<!-- Output: JOHN DOE -->
lowercaseβ
{{lowercase email}}
<!-- Output: user@example.com -->
currencyβ
{{currency price}}
<!-- Output: $99.99 -->
Add Custom Helperβ
Edit utils/helpers/handlebars.helper.ts:
import Handlebars from 'handlebars';
Handlebars.registerHelper('customHelper', function(value) {
// Custom logic
return value.toUpperCase();
});
Best Practicesβ
1. Always Use Queue for Productionβ
// β
Good - Non-blocking
await mailService.queueMail({ ... });
// β Bad - Blocks request
await mailService.sendMail({ ... });
2. Handle Errors Gracefullyβ
try {
await mailService.queueMail({
to: user.email,
subject: 'Welcome',
template: 'activation',
context: { user_name: user.name },
});
} catch (error) {
console.error('Failed to queue email:', error);
// Log error but don't fail the request
}
3. Use Meaningful Subjectsβ
// β
Good
subject: 'Welcome to Express Base API - Activate Your Account'
// β Bad
subject: 'Activation'
4. Test Templates Thoroughlyβ
Always test email rendering across different clients:
- Gmail
- Outlook
- Apple Mail
- Mobile devices
5. Personalize Emailsβ
// β
Good - Personalized
context: {
user_name: `${user.first_name} ${user.last_name}`,
account_type: user.subscription,
}
// β Bad - Generic
context: {
user_name: 'User',
}
Troubleshootingβ
Emails Not Sendingβ
Check:
- SMTP credentials correct in
.env USE_MAIL=yesin environment- No firewall blocking SMTP port
- Check queue status if using queues
"Invalid login" Errorβ
Solution:
- Gmail: Use App Password, not account password
- Enable "Less secure app access" (not recommended)
- Check 2FA settings
Emails in Spamβ
Solutions:
- Set up SPF, DKIM, DMARC records
- Use verified domain
- Include unsubscribe link
- Avoid spam trigger words
Template Not Foundβ
Check:
- Template file exists in
templates/directory - File extension is
.hbs - Path is correct (e.g.,
auth/activation, not/auth/activation.hbs)
Next Stepsβ
- Authentication - Email notifications for auth events
- Custom Templates - Advanced template customization
- Queue System - Deep dive into Bull queues
Questions? Check the GitHub Discussions or open an issue.