Email Service
The Email Service component provides a production-ready solution for sending emails in ServerCN using nodemailer. It supports multiple email providers including Gmail, SMTP servers, SendGrid, and more.
Features
- Multiple email providers - Gmail, SMTP, SendGrid, AWS SES, and more
- HTML and plain text emails - Support for both HTML and text content
- Attachments - Send files as email attachments
- Multiple recipients - Support for CC, BCC, and multiple recipients
- Type-safe - Full TypeScript support
- Error handling - Comprehensive error handling and validation
- Email verification - Verify email configuration before sending
Installation Guide
1. Install ServerCN dependencies
For a fully structured setup with consistent error handling and standardized API responses:
- HTTP Status Codes:
npx servercn add http-status-codesDocumentation: HTTP Status Codes
- Api Response Handler:
npx servercn add response-formatterDocumentation: Api Response Handler:
- Api Error Handler:
npx servercn add error-handlerDocumentation: Api Error Handler:
- Async Handler:
npx servercn add async-handlerDocumentation: Async Handler
3. Install this component
npx servercn add email-servicePrerequisites
Gmail Setup (Recommended for Development)
- Enable 2-Step Verification on your Google Account
- Generate an App Password:
- Go to Google Account → Security → 2-Step Verification → App passwords
- Create a new app password for "Mail"
- Copy the generated password
Environment Variables
Add the following to your .env file:
EMAIL_HOST="smtp.gmail.com"
EMAIL_PORT="587"
EMAIL_SECURE="false"
EMAIL_USER="your-email@gmail.com"
EMAIL_PASSWORD="your-app-password"
EMAIL_FROM="your-email@gmail.com"Other Email Providers
SMTP Server
EMAIL_HOST="smtp.example.com"
EMAIL_PORT="587"
EMAIL_SECURE="false"
EMAIL_USER="your-username"
EMAIL_PASSWORD="your-password"
EMAIL_FROM="noreply@example.com"SendGrid
EMAIL_HOST="smtp.sendgrid.net"
EMAIL_PORT="587"
EMAIL_SECURE="false"
EMAIL_USER="apikey"
EMAIL_PASSWORD="your-sendgrid-api-key"
EMAIL_FROM="noreply@yourdomain.com"AWS SES
EMAIL_HOST="email-smtp.us-east-1.amazonaws.com"
EMAIL_PORT="587"
EMAIL_SECURE="false"
EMAIL_USER="your-aws-access-key-id"
EMAIL_PASSWORD="your-aws-secret-access-key"
EMAIL_FROM="noreply@yourdomain.com"Basic Implementation
1. MVC Structure
Create email service in src/services/email.service.ts:
import nodemailer from "nodemailer";
import env from "../configs/env";
const transporter = nodemailer.createTransport({
host: env.EMAIL_HOST,
port: env.EMAIL_PORT,
secure: env.EMAIL_SECURE,
auth: {
user: env.EMAIL_USER,
pass: env.EMAIL_PASSWORD
}
});
export async function sendEmail(options: {
to: string | string[];
subject: string;
text?: string;
html?: string;
}) {
const info = await transporter.sendMail({
from: env.EMAIL_FROM,
to: Array.isArray(options.to) ? options.to.join(", ") : options.to,
subject: options.subject,
text: options.text,
html: options.html
});
return info;
}2. Feature-Based Structure
For feature-based architecture, place service in src/modules/email/email.service.ts:
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST!,
port: Number(process.env.EMAIL_PORT) || 587,
secure: process.env.EMAIL_SECURE === "true",
auth: {
user: process.env.EMAIL_USER!,
pass: process.env.EMAIL_PASSWORD!
}
});
// Same functions as MVC structureUsage Examples
1. Send Plain Text Email
import { sendTextEmail } from "../services/email.service";
await sendTextEmail(
"user@example.com",
"Welcome to ServerCN",
"Thank you for joining us!"
);2. Send HTML Email
import { sendHtmlEmail } from "../services/email.service";
const htmlContent = `
<h1>Welcome to ServerCN</h1>
<p>Thank you for joining us!</p>
<a href="https://servercn.com">Visit our website</a>
`;
await sendHtmlEmail("user@example.com", "Welcome to ServerCN", htmlContent);3. Send Email with Multiple Recipients
import { sendEmail } from "../services/email.service";
await sendEmail({
to: ["user1@example.com", "user2@example.com"],
subject: "Team Update",
html: "<p>This is a team update.</p>",
cc: ["manager@example.com"],
bcc: ["archive@example.com"]
});4. Send Email with Attachments
import { sendEmail } from "../services/email.service";
import fs from "fs";
await sendEmail({
to: "user@example.com",
subject: "Invoice",
html: "<p>Please find your invoice attached.</p>",
attachments: [
{
filename: "invoice.pdf",
path: "./invoices/invoice-123.pdf"
},
{
filename: "logo.png",
content: fs.readFileSync("./assets/logo.png"),
contentType: "image/png"
}
]
});5. Send Email from Route Handler
import { Request, Response } from "express";
import { asyncHandler } from "../middlewares/async-handler";
import { sendHtmlEmail } from "../services/email.service";
import { ApiResponse } from "../utils/api-response";
import { ApiError } from "../utils/error-handler";
import { STATUS_CODES } from "../constants/status-codes";
export const sendWelcomeEmail = asyncHandler(
async (req: Request, res: Response) => {
const { email, name } = req.body;
if (!email) {
throw new ApiError(STATUS_CODES.BAD_REQUEST, "Email address is required");
}
const htmlContent = `
<h1>Welcome ${name || "User"}!</h1>
<p>Thank you for joining ServerCN.</p>
`;
await sendHtmlEmail(email, "Welcome to ServerCN", htmlContent);
return res
.status(STATUS_CODES.OK)
.json(ApiResponse.success(null, "Welcome email sent successfully"));
}
);6. Verify Email Configuration
import { verifyEmailConfig } from "../services/email.service";
const isValid = await verifyEmailConfig();
if (!isValid) {
console.error("Email configuration is invalid");
}7. Send Transactional Emails
import { sendHtmlEmail } from "../services/email.service";
// Password reset email
export async function sendPasswordResetEmail(
email: string,
resetToken: string
) {
const resetUrl = `https://yourapp.com/reset-password?token=${resetToken}`;
const html = `
<h2>Password Reset Request</h2>
<p>Click the link below to reset your password:</p>
<a href="${resetUrl}">Reset Password</a>
<p>This link will expire in 1 hour.</p>
`;
await sendHtmlEmail(email, "Reset Your Password", html);
}
// Email verification
export async function sendVerificationEmail(
email: string,
verificationToken: string
) {
const verifyUrl = `https://yourapp.com/verify-email?token=${verificationToken}`;
const html = `
<h2>Verify Your Email</h2>
<p>Click the link below to verify your email address:</p>
<a href="${verifyUrl}">Verify Email</a>
`;
await sendHtmlEmail(email, "Verify Your Email", html);
}Email Templates
Using Template Engines
You can use template engines like Handlebars, EJS, or Pug:
import { sendHtmlEmail } from "../services/email.service";
import handlebars from "handlebars";
import fs from "fs";
const templateSource = fs.readFileSync("./templates/welcome.hbs", "utf8");
const template = handlebars.compile(templateSource);
const html = template({
name: "John Doe",
activationLink: "https://yourapp.com/activate"
});
await sendHtmlEmail("user@example.com", "Welcome!", html);Error Handling
The service includes comprehensive error handling:
import { sendEmail } from "../services/email.service";
import { ApiError } from "../utils/error-handler";
try {
await sendEmail({
to: "user@example.com",
subject: "Test",
html: "<p>Test email</p>"
});
} catch (error) {
if (error instanceof ApiError) {
// Handle API errors
console.error("Email error:", error.message);
} else {
// Handle other errors
console.error("Unexpected error:", error);
}
}Best Practices
- Use environment variables - Never hardcode email credentials
- Validate email addresses - Validate recipient emails before sending
- Handle errors gracefully - Implement proper error handling and logging
- Use HTML templates - Use template engines for consistent email design
- Rate limiting - Implement rate limiting for email endpoints
- Email queuing - Use job queues (Bull, BullMQ) for bulk emails
- Test emails - Use services like Mailtrap for testing in development
Common Email Providers Configuration
Gmail
EMAIL_HOST="smtp.gmail.com"
EMAIL_PORT="587"
EMAIL_SECURE="false"Outlook/Office 365
EMAIL_HOST="smtp.office365.com"
EMAIL_PORT="587"
EMAIL_SECURE="false"Yahoo Mail
EMAIL_HOST="smtp.mail.yahoo.com"
EMAIL_PORT="587"
EMAIL_SECURE="false"Custom SMTP
EMAIL_HOST="smtp.yourdomain.com"
EMAIL_PORT="587"
EMAIL_SECURE="false"Security Considerations
- App Passwords - Use app-specific passwords for Gmail
- Environment Variables - Store credentials securely
- Rate Limiting - Prevent email spam and abuse
- Input Validation - Validate email addresses and content
- HTTPS - Use secure connections (EMAIL_SECURE=true for port 465)
Troubleshooting
"Invalid login" Error
- Verify your email credentials
- For Gmail, ensure you're using an App Password, not your regular password
- Check that 2-Step Verification is enabled
"Connection timeout" Error
- Verify EMAIL_HOST and EMAIL_PORT are correct
- Check firewall settings
- Ensure EMAIL_SECURE matches your port (true for 465, false for 587)
Emails Not Received
- Check spam/junk folder
- Verify EMAIL_FROM address
- Check email provider's sending limits
- Verify recipient email address is valid