Lesson 25 of 49 intermediate

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

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. 1. Email service — the core logic 📮
  2. 2. Decorator that adds metadata or behavior
  3. 3. Exporting for use in other files
  4. 4.
  5. 5. Decorator that adds metadata or behavior
  6. 6.
  7. 7.
  8. 8.
  9. 9. Queue an email (returns instantly!)
  10. 10.
  11. 11.
  12. 12.
  13. 13.
  14. 14.
  15. 15.
  16. 16.
  17. 17.
  18. 18.
  19. 19.
  20. 20.
  21. 21.
  22. 22.
  23. 23.
  24. 24.
  25. 25.
  26. 26.
  27. 27. 2. Email processor — sends in background 🏭
  28. 28. Decorator that adds metadata or behavior
  29. 29. Exporting for use in other files
  30. 30.
  31. 31.
  32. 32. Decorator that adds metadata or behavior
  33. 33.
  34. 34.
  35. 35.
  36. 36.
  37. 37.
  38. 38.
  39. 39.
  40. 40.
  41. 41.
  42. 42.
  43. 43. 3. React Email template — looks like React! ⚛️
  44. 44. Exporting for use in other files
  45. 45.
  46. 46.
  47. 47.
  48. 48.
  49. 49.
  50. 50.
  51. 51.
  52. 52.
  53. 53.
  54. 54.
  55. 55.
  56. 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

Open interactive version (quiz + challenge) ← Back to course: Full-Stack Playbook