Skip to main content

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',
},
});
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 name
  • activation_code - 6-digit OTP
  • app_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 name
  • login_time - Login timestamp
  • device - Device type (Desktop, Mobile, Tablet)
  • browser - Browser name
  • location - City/location
  • ip_address - IP address
  • security_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 name
  • reset_link - Password reset link with token
  • expiry_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 name
  • otp_code - 6-digit OTP
  • expiry_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 name
  • alert_reason - Why alert was triggered
  • reset_link - Password reset link
  • support_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>&copy; 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:

  1. SMTP credentials correct in .env
  2. USE_MAIL=yes in environment
  3. No firewall blocking SMTP port
  4. 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​


Questions? Check the GitHub Discussions or open an issue.