Envío de Emails
Implementaremos envío de emails transaccionales con Nodemailer.
Instalación
bash
npm install nodemailernpm install -D @types/nodemailerConfiguración
typescript
// src/config/mail.tsimport nodemailer from 'nodemailer'; const transporter = nodemailer.createTransport({ host: process.env.MAIL_HOST, port: Number(process.env.MAIL_PORT), secure: false, auth: { user: process.env.MAIL_USER, pass: process.env.MAIL_PASS, },}); export { transporter };Mail Service
typescript
// src/services/mail.service.tsimport { transporter } from '../config/mail'; interface SendMailOptions { to: string; subject: string; html: string;} export class MailService { async send({ to, subject, html }: SendMailOptions) { await transporter.sendMail({ from: '"E-commerce" <no-reply@ecommerce.com>', to, subject, html, }); } async sendWelcome(user: { email: string; name: string }) { await this.send({ to: user.email, subject: 'Bienvenido!', html: ` <h1>Hola ${user.name}!</h1> <p>Tu cuenta ha sido creada con éxito.</p> `, }); } async sendPasswordReset(email: string, token: string) { const resetUrl = `${process.env.FRONTEND_URL}/reset-password?token=${token}`; await this.send({ to: email, subject: 'Restablecer contraseña', html: ` <h1>Restablecer contraseña</h1> <p>Haz clic en el enlace para restablecer:</p> <a href="${resetUrl}">Restablecer contraseña</a> <p>Este enlace expira en 1 hora.</p> `, }); } async sendOrderConfirmation(order: OrderWithItems) { const itemsList = order.items.map(item => `<li>${item.product.name} x${item.quantity} - $${item.price}</li>` ).join(''); await this.send({ to: order.user.email, subject: `Pedido #${order.id.slice(0, 8)} confirmado`, html: ` <h1>Gracias por tu compra!</h1> <p>Tu pedido ha sido confirmado:</p> <ul>${itemsList}</ul> <p><strong>Total: $${order.total}</strong></p> `, }); }} export const mailService = new MailService();Templates con Handlebars
typescript
// src/config/mail-templates.tsimport Handlebars from 'handlebars';import fs from 'fs';import path from 'path'; const templatesDir = path.resolve(__dirname, '..', 'templates', 'emails'); export const compileTemplate = (templateName: string, data: object) => { const templatePath = path.join(templatesDir, `${templateName}.hbs`); const templateSource = fs.readFileSync(templatePath, 'utf8'); const template = Handlebars.compile(templateSource); return template(data);}; // templates/emails/welcome.hbs// <h1>Hola {{name}}!</h1>// <p>Bienvenido a nuestra plataforma.</p>Envío en Background
typescript
// Usando con Bull (colas)import Queue from 'bull'; const emailQueue = new Queue('email', process.env.REDIS_URL!); emailQueue.process(async (job) => { const { type, data } = job.data; switch (type) { case 'welcome': await mailService.sendWelcome(data); break; case 'order': await mailService.sendOrderConfirmation(data); break; }}); // Agregar a la colaawait emailQueue.add({ type: 'welcome', data: { email, name } });Resumen
- ✅ Nodemailer configurado
- ✅ Emails transaccionales
- ✅ Templates HTML
- ✅ Colas para background
Próxima clase: Cache con Redis! 🚀