Email Services
Sending Emails From Code
Open interactive version (quiz + challenge)Real-world analogy
Sending emails from code is like having a robot assistant. You tell the robot WHAT to say (template), WHO to send it to (recipient), and WHICH postal service to use (AWS SES, Mailgun, SendGrid). The robot handles the rest!
What is it?
Email services in a NestJS app involve an email provider (like AWS SES), template system (like React Email or Handlebars), and a queue (Bull) to send emails asynchronously without blocking the user.
Real-world relevance
Every SaaS product sends emails: welcome messages, password resets, invoices, notifications. A typical app sends thousands of emails daily through providers like SES or SendGrid.
Key points
- Email Providers — AWS SES, Mailgun, and SendGrid are postal services that deliver your emails. They handle spam filtering, delivery tracking, and bounce management. SES is cheapest at scale, SendGrid has the best dashboard.
- Templates — Pre-designed layouts for welcome emails, password resets, order confirmations, and invoices. Use Handlebars, EJS, or React Email for reusable templates with dynamic placeholders filled at send time.
- Queued Sending — Never send emails directly in your API handler — it blocks users for 2-5 seconds. Push email jobs to a Bull queue and return instantly. A background worker sends it later, keeping API responses fast.
- React Email — Build responsive email templates using React components instead of wrestling with inline CSS and table layouts. React Email provides Html, Body, Button, and Text components that render to email-compatible HTML.
- Unsubscribe & Preferences — Marketing emails must include an unsubscribe link — required by law (CAN-SPAM, GDPR). Let users manage preferences: order updates yes, newsletters no. Store and check preferences before sending.
- Transactional vs Bulk — Transactional emails (password reset, order confirmation) are triggered by user actions and sent immediately. Bulk emails (newsletters) can be batched. Use separate queues so transactional mail is never delayed.
- Email Deliverability — Getting emails to the inbox requires SPF, DKIM, and DMARC DNS records for your domain. Use a consistent sender address, avoid spammy subjects, and maintain sender reputation. Monitor bounce rates closely.
- Error Handling & Retries — Emails can fail from invalid addresses, provider outages, or rate limits. Implement retry logic with exponential backoff in your Bull processor. Log failures with error details and set up alerts for spikes.
- Email Testing — Use Ethereal or Mailtrap during development to catch emails without sending to real users. They provide fake SMTP servers that capture every email, letting you preview HTML, check headers, and verify links.
- Attachments & Inline Images — Send PDF invoices, CSV reports, or images as attachments using Buffer or streams. For inline images, use Content-ID references. Keep attachments under 10MB and link to S3 for larger documents.
Code example
// 1. Email service — the core logic 📮
@Injectable()
export class EmailService {
constructor(
@InjectQueue('email') private emailQueue: Queue,
private configService: ConfigService,
) {}
// Queue an email (returns instantly!)
async sendWelcomeEmail(user: User) {
await this.emailQueue.add('welcome', {
to: user.email,
name: user.name,
template: 'welcome',
});
}
async sendPasswordReset(email: string, token: string) {
await this.emailQueue.add('password-reset', {
to: email,
resetUrl: `https://myapp.com/reset?token=${token}`,
template: 'password-reset',
});
}
}
// 2. Email processor — sends in background 🏭
@Processor('email')
export class EmailProcessor {
constructor(private mailer: MailerService) {}
@Process('welcome')
async sendWelcome(job: Job) {
const { to, name } = job.data;
await this.mailer.send({
to,
subject: `Welcome aboard, ${name}! 🎉`,
html: renderWelcomeTemplate({ name }),
});
}
}
// 3. React Email template — looks like React! ⚛️
export function WelcomeEmail({ name }: { name: string }) {
return (
<Html>
<Body style={{ fontFamily: 'Arial' }}>
<Heading>Welcome, {name}!</Heading>
<Text>We're thrilled to have you.</Text>
<Button href="https://myapp.com/dashboard">
Get Started
</Button>
</Body>
</Html>
);
}Line-by-line walkthrough
- 1. 1. Email service — the core logic 📮
- 2. Decorator that adds metadata or behavior
- 3. Exporting for use in other files
- 4.
- 5. Decorator that adds metadata or behavior
- 6.
- 7.
- 8.
- 9. Queue an email (returns instantly!)
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27. 2. Email processor — sends in background 🏭
- 28. Decorator that adds metadata or behavior
- 29. Exporting for use in other files
- 30.
- 31.
- 32. Decorator that adds metadata or behavior
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43. 3. React Email template — looks like React! ⚛️
- 44. Exporting for use in other files
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
Spot the bug
async sendReset(email: string) {
const token = generateToken();
await this.mailer.send({
to: email,
subject: "Reset",
html: "<a href='myapp.com/reset?token=" + token + "'>Reset</a>",
});
}Need a hint?
What if sending fails? Should this block the request?
Show answer
Two issues: 1) Email is sent synchronously, making the user wait. 2) No error handling if sending fails. Fix: use a Bull queue for background sending and wrap in try/catch.
Explain like I'm 5
Sending emails from code is like having a robot pen pal. You tell the robot who to write to, what to say, and which post office to use. The robot writes the letter, puts it in an envelope, and sends it - all without you licking a single stamp!
Fun fact
Gmail processes over 1.8 billion emails per day. If you printed them all on paper, the stack would reach the Moon and back... every single day! 🌙
Hands-on challenge
Use Nodemailer with a free Ethereal account (ethereal.email) to send a test email from Node.js. Check the 'caught' email in Ethereal's inbox!
More resources
- Nodemailer Documentation (Nodemailer Official)
- NestJS Mailer Module (NestJS Modules)
- React Email Documentation (React Email)