Never block your API response waiting for an email to send! Email delivery can take 1-5 seconds (or more). If you wait for the email before sending the HTTP 200 response, your API becomes slow and unreliable. If the email server is down, your order endpoint also fails.
BAD — Blocking the API response:
// ❌ BAD: Email bhejne tak response block karo
app.post('/api/orders', async (req, res) => {
const order = await Order.create(req.body);
// WAITING for email... (1-5 seconds!)
await transporter.sendMail(mailOptions);
// Response sent AFTER email is done
res.json({ order }); // User waits 5 seconds! 😭
});
GOOD — Async (fire and forget):
// ✅ GOOD: Pehle response bhejo, fir email bhejo
app.post('/api/orders', async (req, res) => {
const order = await Order.create(req.body);
// IMMEDIATELY send response! (no await on email)
res.status(201).json({ order });
// Email bhejna — response ke BAAD, no blocking!
sendOrderConfirmation(order, user)
.then(result => console.log('Email result:', result))
.catch(err => console.error('Email failed:', err.message));
// Even if email fails, order is still confirmed!
});
BEST — Use a queue (production-ready):
// services/email.queue.js — Bull Queue for Async Email Processing
import Queue from 'bull';
// Redis-backed email queue
const emailQueue = new Queue('email-notifications', process.env.REDIS_URL);
// Process emails in background
emailQueue.process(async (job) => {
const { order, user, type } = job.data;
if (type === 'order-confirmation') {
await sendOrderConfirmation(order, user);
} else if (type === 'password-reset') {
await sendPasswordResetEmail(user, job.data.token);
}
});
// Usage in routes — just add to queue, response is instant!
app.post('/api/orders', async (req, res) => {
const order = await Order.create(req.body);
// Add email job to queue (instant — ~1ms)
await emailQueue.add({
type: 'order-confirmation',
order,
user
});
// Response sent immediately
res.status(201).json({ order });
});
Why use a queue?
- Speed — API response in milliseconds, not seconds
- Reliability — If email fails, queue retries automatically
- Scalability — Queue worker can run on a separate server
- Back-pressure — Queue handles email spikes gracefully
For this chapter, we'll use the simple "fire and forget" async approach. In production, always use a queue system (Bull, RabbitMQ, AWS SQS).