Aller au contenu principal

Email Templates with Handlebars

JifiJs uses Handlebars as its templating engine for creating beautiful, dynamic HTML emails. This guide shows you how to create, customize, and send professional email templates.

Template Structure

Directory Layout

templates/
├── layouts/
│ └── default.hbs # Base layout
├── emails/
│ ├── welcome.hbs # Welcome email
│ ├── password-reset.hbs # Password reset
│ └── verify-email.hbs # Email verification
└── ...

Creating Templates

Basic Template

Create templates/emails/welcome.hbs:

<h2>Welcome, {{name}}!</h2>

<p>Thanks for joining {{app_name}}! We're excited to have you on board.</p>

<p>To get started, please verify your email address:</p>

<p style="text-align: center; margin: 30px 0;">
<a href="{{verificationLink}}" style="
display: inline-block;
padding: 12px 24px;
background: #6366f1;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: 600;
">Verify Email Address</a>
</p>

<p>If you didn't create this account, you can safely ignore this email.</p>

<p>Best regards,<br>The {{app_name}} Team</p>

Using Layouts

JifiJs uses a layout system. Create a reusable layout in templates/layouts/default.hbs:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
<style>
body {
font-family: 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
color: white;
padding: 30px;
text-align: center;
border-radius: 8px 8px 0 0;
}
.content {
background: #ffffff;
padding: 30px;
border: 1px solid #e5e7eb;
}
.footer {
text-align: center;
padding: 20px;
color: #6b7280;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
{{{body}}}
</div>
</body>
</html>

The {{{body}}} placeholder will be replaced with your template content.

Sending Emails with MailService

JifiJs provides a built-in MailService for sending emails.

Basic Email Sending

import mailService from './utils/bases/mail.service';

// Send email immediately
await mailService.send(
'user@example.com', // receivers
'Welcome to JifiJs', // subject
'emails/welcome', // template path
{ // template data
name: 'John Doe',
verificationLink: 'https://example.com/verify?token=abc123',
}
);

Sending with Queue

For better performance, use queue-based sending:

import mailService from './utils/bases/mail.service';

// Send email via queue (recommended for production)
await mailService.sendWithQueue(
'user@example.com',
'Welcome to JifiJs',
'emails/welcome',
{
name: 'John Doe',
verificationLink: 'https://example.com/verify?token=abc123',
}
);

Custom Sender

await mailService.send(
'user@example.com',
'Important Notification',
'emails/notification',
{ message: 'Your account has been updated' },
{
name: 'Support Team',
email: 'support@example.com',
}
);

Email with Attachments

await mailService.send(
'user@example.com',
'Your Invoice',
'emails/invoice',
{ invoiceNumber: 'INV-001' },
null, // use default sender
[ // attachments
{
filename: 'invoice.pdf',
path: '/path/to/invoice.pdf',
},
]
);

Plain Text Email

Send email without template (plain text):

await mailService.send(
'user@example.com',
'Test Email',
'This is a plain text email', // content (not a template path)
null // null = no template rendering
);

Template Data

Available Variables

JifiJs automatically provides these variables to all templates:

{
app_name: string; // From APP_NAME env variable
app_settings: { // Application configuration
name: string;
mode: string;
port: number;
url: string;
// ... other app settings
};
// ... your custom data
}

Example Usage

<h1>Welcome to {{app_name}}</h1>
<p>Environment: {{app_settings.mode}}</p>
<p>Application URL: {{app_settings.url}}</p>

Handlebars Helpers

JifiJs registers custom Handlebars helpers for common tasks.

Date Formatting

{{formatDate createdAt "MM/DD/YYYY"}}

Conditional Rendering

{{#if isPremium}}
<p>Thank you for being a premium member!</p>
{{else}}
<p>Upgrade to premium for more features.</p>
{{/if}}

Loops

<h3>Your Recent Orders</h3>
<ul>
{{#each orders}}
<li>Order #{{this.orderNumber}} - {{this.total}}</li>
{{/each}}
</ul>

{{#unless orders.length}}
<p>You haven't placed any orders yet.</p>
{{/unless}}

Email Configuration

Environment Variables

Configure email settings in .env:

Development Mode (APP_MODE=DEVELOPMENT)

Uses _DEV suffix variables:

MAIL_HOST_DEV=localhost
MAIL_PORT_DEV=1025
MAIL_SERVICE_DEV=gmail
MAIL_SENSER_NAME_DEV="Development Team"
MAIL_SENSER_EMAIL_DEV="dev@example.com"
MAIL_SECURE_DEV=false
MAIL_USER_DEV=false
MAIL_PASSWORD_DEV=false

Production Mode (APP_MODE=PRODUCTION)

Uses production variables:

MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_SERVICE=gmail
MAIL_SENSER_NAME="Company Name"
MAIL_SENSER_EMAIL="noreply@example.com"
MAIL_SECURE=true
MAIL_USER=your-email@gmail.com
MAIL_PASSWORD=your-app-password

Template Path

TEMPLATE_PATH=templates

Responsive Email Design

Mobile-Friendly Template

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.container {
max-width: 600px;
margin: 0 auto;
}

@media only screen and (max-width: 600px) {
.container {
width: 100% !important;
padding: 10px !important;
}

.button {
display: block !important;
width: 100% !important;
text-align: center !important;
}

h1 {
font-size: 24px !important;
}
}
</style>
</head>
<body>
<div class="container">
{{{body}}}
</div>
</body>
</html>

Email-Safe CSS

Best Practices

Recommended:

  • Use inline styles for maximum compatibility
  • Use tables for layout instead of divs
  • Use web-safe fonts (Arial, Helvetica, Times New Roman)
  • Specify full hex colors (#000000 instead of #000)
  • Use padding instead of margin

Avoid:

  • Flexbox/grid layouts (poor email client support)
  • Background images (often blocked)
  • JavaScript (completely blocked)
  • External CSS (often stripped)
  • Video/audio elements (not supported)

Inline Styles Example

<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td style="
background-color: #6366f1;
color: #ffffff;
padding: 20px;
text-align: center;
font-family: Arial, sans-serif;
">
<h1 style="margin: 0; font-size: 28px;">
{{title}}
</h1>
</td>
</tr>
</table>

Testing Emails

Local Testing with MailHog

For development, use MailHog to catch all outgoing emails:

# Install MailHog
brew install mailhog # macOS
# or download from https://github.com/mailhog/MailHog

# Run MailHog
mailhog

# Configure .env for development
MAIL_HOST_DEV=localhost
MAIL_PORT_DEV=1025

Access MailHog UI at http://localhost:8025

Testing Tools

Queue Management

Check Queue Status

const stats = await mailService.getQueueStats();
console.log(stats);
// {
// waiting: 10,
// active: 2,
// completed: 145,
// failed: 3,
// delayed: 5
// }

Get Failed Jobs

const failedJobs = await mailService.getFailedJobs();
console.log(failedJobs);

Retry Failed Jobs

// Retry specific job
await mailService.retryFailedJob('job-id');

// Retry all failed jobs
await mailService.retryAllFailedJobs();

Clean Old Jobs

// Clean completed jobs older than 24 hours
await mailService.cleanCompletedJobs(24 * 60 * 60 * 1000);

// Clean failed jobs older than 7 days
await mailService.cleanFailedJobs(7 * 24 * 60 * 60 * 1000);

Common Email Templates

Welcome Email

<h2>Welcome to {{app_name}}, {{name}}!</h2>

<p>Thanks for joining us. We're excited to have you on board.</p>

<p>To get started, please verify your email address:</p>

<p style="text-align: center;">
<a href="{{verificationLink}}" style="
display: inline-block;
padding: 14px 28px;
background: #6366f1;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: 600;
">Verify Email</a>
</p>

Password Reset

<h2>Password Reset Request</h2>

<p>Hi {{name}},</p>

<p>We received a request to reset your password. Click the button below:</p>

<p style="text-align: center;">
<a href="{{resetLink}}" style="
display: inline-block;
padding: 14px 28px;
background: #ef4444;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: 600;
">Reset Password</a>
</p>

<p style="color: #dc2626;">
<strong>This link expires in 1 hour.</strong>
</p>

<p>If you didn't request this, ignore this email.</p>

Email Verification

<h2>Verify Your Email</h2>

<p>Hi {{name}},</p>

<p>Please verify your email address to activate your account:</p>

<p style="text-align: center;">
<a href="{{verificationLink}}" style="
display: inline-block;
padding: 14px 28px;
background: #10b981;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: 600;
">Verify Email</a>
</p>

<p>Or copy and paste this link:</p>
<p style="color: #6b7280; word-break: break-all;">{{verificationLink}}</p>

Troubleshooting

Email Not Sending

Check the mail queue status:

const stats = await mailService.getQueueStats();
if (stats.failed > 0) {
const failedJobs = await mailService.getFailedJobs();
console.log('Failed jobs:', failedJobs);
}

Gmail Authentication

For Gmail, you need an App Password:

  1. Enable 2FA on your Google account
  2. Generate App Password at https://myaccount.google.com/apppasswords
  3. Use the App Password (not your regular password) in MAIL_PASSWORD

SMTP Connection Errors

The MailService automatically detects connection errors and won't retry permanently failed connections. Check your SMTP settings:

# Test SMTP connection
telnet smtp.gmail.com 587

Pro Tip: Always use queue-based sending (sendWithQueue) in production for better performance and reliability. The queue system automatically handles retries and prevents email sending from blocking your API responses.