Pular para o conteúdoPedro Farbo
Lição 15 / 2535 min

Envio de E-mails

Envio de E-mails

Enviar e-mails é essencial para confirmação de conta, recuperação de senha e notificações.

Setup com Nodemailer

bash
npm install nodemailernpm install -D @types/nodemailer

Configuração

typescript
// src/config/mail.tsimport nodemailer from 'nodemailer'; const transporter = nodemailer.createTransport({  host: process.env.MAIL_HOST,  port: Number(process.env.MAIL_PORT),  secure: process.env.MAIL_SECURE === 'true',  auth: {    user: process.env.MAIL_USER,    pass: process.env.MAIL_PASS,  },}); export default transporter;

Serviço de E-mail

typescript
// src/services/mail.service.tsimport transporter from '../config/mail';import { User } from '@prisma/client'; interface SendMailOptions {  to: string;  subject: string;  html: string;} export class MailService {  private from = `"E-commerce" <${process.env.MAIL_FROM}>`;   async send({ to, subject, html }: SendMailOptions): Promise<void> {    await transporter.sendMail({      from: this.from,      to,      subject,      html,    });  }   async sendWelcome(user: User): Promise<void> {    await this.send({      to: user.email,      subject: 'Bem-vindo ao E-commerce!',      html: `        <h1>Olá, ${user.name}!</h1>        <p>Sua conta foi criada com sucesso.</p>        <p>Comece a explorar nossos produtos!</p>      `,    });  }   async sendPasswordReset(user: User, token: string): Promise<void> {    const resetUrl = `${process.env.FRONTEND_URL}/reset-password?token=${token}`;     await this.send({      to: user.email,      subject: 'Recuperação de Senha',      html: `        <h1>Recuperação de Senha</h1>        <p>Você solicitou a recuperação de senha.</p>        <p>Clique no link abaixo para criar uma nova senha:</p>        <a href="${resetUrl}">Redefinir Senha</a>        <p>O link expira em 1 hora.</p>        <p>Se você não solicitou, ignore este e-mail.</p>      `,    });  }   async sendOrderConfirmation(user: User, order: any): Promise<void> {    const itemsHtml = order.items      .map((item: any) => `<li>${item.product.name} x ${item.quantity} - R$ ${item.price}</li>`)      .join('');     await this.send({      to: user.email,      subject: `Pedido #${order.id} Confirmado`,      html: `        <h1>Pedido Confirmado!</h1>        <p>Obrigado pela sua compra, ${user.name}!</p>        <h2>Itens:</h2>        <ul>${itemsHtml}</ul>        <p><strong>Total: R$ ${order.total}</strong></p>      `,    });  }}

Templates com Handlebars

bash
npm install handlebars
typescript
// src/services/mail-template.service.tsimport Handlebars from 'handlebars';import fs from 'fs';import path from 'path'; export class MailTemplateService {  private templatesDir = path.resolve(__dirname, '..', 'templates', 'emails');   compile(templateName: string, data: Record<string, any>): string {    const templatePath = path.join(this.templatesDir, `${templateName}.hbs`);    const source = fs.readFileSync(templatePath, 'utf-8');    const template = Handlebars.compile(source);    return template(data);  }}
handlebars
<!-- src/templates/emails/welcome.hbs --><!DOCTYPE html><html><head>  <style>    .container { max-width: 600px; margin: 0 auto; font-family: Arial; }    .header { background: #4F46E5; color: white; padding: 20px; }    .content { padding: 20px; }    .button { background: #4F46E5; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; }  </style></head><body>  <div class="container">    <div class="header">      <h1>Bem-vindo!</h1>    </div>    <div class="content">      <p>Olá, {{name}}!</p>      <p>Sua conta foi criada com sucesso.</p>      <a href="{{shopUrl}}" class="button">Começar a Comprar</a>    </div>  </div></body></html>

Filas com Bull

bash
npm install bullnpm install -D @types/bull
typescript
// src/jobs/mail.job.tsimport Bull from 'bull';import { MailService } from '../services/mail.service'; interface MailJobData {  to: string;  subject: string;  html: string;} const mailQueue = new Bull<MailJobData>('mail', {  redis: process.env.REDIS_URL,}); const mailService = new MailService(); mailQueue.process(async (job) => {  await mailService.send(job.data);}); export const addMailJob = (data: MailJobData) => {  return mailQueue.add(data, {    attempts: 3,    backoff: { type: 'exponential', delay: 2000 },  });};

Resumo

  • ✅ Nodemailer para envio
  • ✅ Templates HTML com Handlebars
  • ✅ E-mails transacionais (boas-vindas, pedido)
  • ✅ Filas para envio assíncrono

Próxima aula: Cache com Redis! ⚡

Gostou do conteúdo? Sua contribuição ajuda a manter tudo online e gratuito!

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e