Email & Notifications

15 questions found

What is Nodemailer, and how does it let a Node.js application send emails via SMTP?

Beginner
Nodemailer is the most widely used Node.js library for sending email, supporting SMTP (talking directly to a mail server or provider like Gmail, SendGrid, or Amazon SES over the standard email protocol) as well as several transport methods -- it handles the details of MIME message formatting, attachments, and HTML/plain-text bodies behind a simple, promise-based API.
const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: 'smtp.sendgrid.net',
  port: 587,
  auth: { user: 'apikey', pass: process.env.SENDGRID_API_KEY },
});

await transporter.sendMail({
  from: 'noreply@example.com',
  to: user.email,
  subject: 'Welcome!',
  html: '<h1>Welcome to our platform</h1>',
});
Real-world example A SaaS application uses Nodemailer configured with SendGrid's SMTP relay to send transactional emails like welcome messages and password resets, relying on SendGrid's infrastructure for actual delivery rather than managing its own mail server.

Common follow-ups: Why do most production applications use a third-party email provider (SendGrid, SES) rather than sending directly from their own server via SMTP?;How would you handle and log a failed email send without blocking the request that triggered it?

Background Jobs & Queues;HTTP & HTTPS Modules

Why should sending an email typically happen in a background job rather than synchronously within an HTTP request handler?

Intermediate
Sending an email involves a network call to an external mail provider that can be slow or occasionally fail -- doing this synchronously inside a request handler makes the user wait for that external call to complete (or fail) before getting a response, and ties the success of the user's core action (like registering an account) to the reliability of the email provider; enqueuing the email send as a background job lets the main request return immediately while a worker handles the actual sending, with its own independent retry logic.
// Blocking: the user waits for the email to send before getting a response
app.post('/register', async (req, res) => {
  const user = await createUser(req.body);
  await sendWelcomeEmail(user); // slow, and a failure here fails the whole request
  res.json(user);
});

// Better: enqueue and respond immediately
app.post('/register', async (req, res) => {
  const user = await createUser(req.body);
  await emailQueue.add('welcome-email', { userId: user.id });
  res.json(user);
});
Real-world example A registration endpoint that occasionally timed out during a third-party email provider's brief outages was fixed by moving the welcome-email send into a background job queue, so registration succeeds independently of email-provider availability, with the email itself retried automatically by the worker.

Common follow-ups: How would you notify a user if their welcome email ultimately fails after all configured retries?;What's the tradeoff of this asynchronous approach for genuinely time-sensitive emails, like a login verification code?

Background Jobs & Queues;Error Handling

What are SPF, DKIM, and DMARC, and why do they matter for a Node.js application's transactional emails actually reaching users' inboxes?

Advanced
SPF (Sender Policy Framework) publishes a DNS record listing which mail servers are authorized to send email on behalf of a domain, letting receiving servers verify a message wasn't spoofed. DKIM (DomainKeys Identified Mail) cryptographically signs outgoing messages, letting receivers verify the message wasn't altered in transit and genuinely originated from the claimed domain. DMARC builds on both, publishing a policy telling receiving servers what to do (quarantine, reject) with messages that fail SPF or DKIM checks -- without properly configured SPF/DKIM/DMARC records, legitimate transactional emails are significantly more likely to be marked as spam or rejected outright.
; Example SPF DNS TXT record
v=spf1 include:sendgrid.net ~all

; Example DMARC DNS TXT record
v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com
Real-world example A company's password-reset emails were landing in users' spam folders at an alarmingly high rate until they properly configured SPF and DKIM records for their sending domain through their email provider's setup process, dramatically improving inbox placement.

Common follow-ups: How does DKIM signing actually work cryptographically to verify a message wasn't tampered with?;What's the difference between DMARC's 'none', 'quarantine', and 'reject' policy modes?

Security;HTTP & HTTPS Modules

How would you send a push notification from a Node.js backend to a mobile app using Firebase Cloud Messaging (FCM)?

Intermediate
The backend uses the Firebase Admin SDK, authenticated with a service account, to send a message targeting a specific device (via a registration token the client app previously obtained and sent to the backend) or a topic (letting multiple subscribed devices receive the same message) -- FCM then handles actual delivery to the device across both iOS and Android.
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert(serviceAccount) });

await admin.messaging().send({
  token: userDeviceToken,
  notification: { title: 'Order Shipped', body: 'Your order is on its way!' },
});
Real-world example An e-commerce app sends a push notification via FCM to a customer's phone the moment their order ships, using the device token the mobile app registered and sent to the backend during login.

Common follow-ups: How do you handle an invalid or expired device token returned by FCM when sending a notification?;What's the difference between targeting a specific device token versus a topic subscription for broadcast notifications?

HTTP & HTTPS Modules;Background Jobs & Queues

How would you design a multi-channel notification system in Node.js that can send the same logical notification via email, SMS, or push depending on user preference?

Advanced
A common approach defines a common Notification interface/abstraction and separate channel-specific adapters (EmailChannel, SmsChannel, PushChannel) each implementing a consistent send() method -- a central notification service looks up the user's preferred channel(s) and delegates to the appropriate adapter(s), letting new channels be added later without touching the calling code, and letting a single logical event (like 'order shipped') fan out to multiple channels if the user has enabled more than one.
class NotificationService {
  constructor(channels) { this.channels = channels; } // { email: EmailChannel, sms: SmsChannel }
  async notify(user, event) {
    const preferredChannels = user.notificationPreferences; // e.g., ['email', 'push']
    await Promise.allSettled(
      preferredChannels.map(ch => this.channels[ch].send(user, event))
    );
  }
}
Real-world example A logistics platform's notification service sends an 'order shipped' event through both email and SMS for customers who've opted into both channels, using Promise.allSettled so a failure in one channel (like an invalid phone number) doesn't prevent the email from still being sent.

Common follow-ups: How does this design apply the Strategy or Adapter pattern discussed earlier in Architecture & Design Patterns?;How would you track and avoid sending duplicate notifications for the same event across multiple channels?

Architecture & Design Patterns;Background Jobs & Queues

What is email template rendering, and how might you use a templating engine to generate dynamic HTML emails in Node.js?

Intermediate
Rather than building HTML email bodies via manual string concatenation, a templating engine (like Handlebars, EJS, or a dedicated email-templating library like MJML) lets you define reusable email layouts with placeholders for dynamic content (a user's name, an order summary), rendered into final HTML at send time -- MJML specifically compiles a simplified markup into HTML/CSS that's been tested to render consistently across the notoriously inconsistent rendering engines used by different email clients.
const mjml2html = require('mjml');
const Handlebars = require('handlebars');

const template = Handlebars.compile(mjmlSource);
const filledMjml = template({ userName: user.name, orderTotal: order.total });
const { html } = mjml2html(filledMjml);

await transporter.sendMail({ to: user.email, subject: 'Order Confirmation', html });
Real-world example A team building order-confirmation emails uses MJML for the layout (guaranteeing consistent rendering across Outlook, Gmail, and Apple Mail) combined with Handlebars for injecting the specific order details, rather than hand-writing fragile, client-inconsistent raw HTML.

Common follow-ups: Why is writing HTML email templates significantly more constrained than writing regular web HTML/CSS?;How would you preview a rendered email template across different email clients before sending it to real users?

HTML & Web Servers;Testing with Jest Mocha & the Node Test Runner

How would you implement rate limiting or throttling for outgoing notifications to avoid overwhelming users or violating a provider's sending limits?

Advanced
Rate limiting for outgoing notifications operates at two levels: respecting the third-party provider's own sending limits (using a concurrency-limited job queue, discussed earlier in Background Jobs & Queues), and respecting a reasonable limit on how many notifications a single user receives within a time window (to avoid notification fatigue or accidentally spamming a user due to a bug), typically tracked via a counter in Redis with a TTL matching the rate-limit window.
async function canSendNotification(userId, maxPerHour = 5) {
  const key = `notif-rate:${userId}`;
  const count = await redis.incr(key);
  if (count === 1) await redis.expire(key, 3600);
  return count <= maxPerHour;
}
Real-world example A social media platform caps push notifications to any single user at five per hour, using a Redis-backed counter, preventing a bug in a batch-processing job from accidentally flooding a user's phone with dozens of duplicate notifications in a short period.

Common follow-ups: How do you decide an appropriate per-user rate limit without being so restrictive that important notifications get suppressed?;How would you prioritize which notifications to send first if a user has hit their rate limit and several are queued?

Background Jobs & Queues;Caching with Redis

What is the difference between transactional email and marketing email, and why do most Node.js applications use separate providers or sending domains for each?

Intermediate
Transactional email is triggered by a specific user action and expected as part of using the product (password resets, order confirmations, receipts) -- it must be delivered reliably and promptly. Marketing email (newsletters, promotional campaigns) is sent in bulk to an opted-in list and is held to different deliverability and compliance standards (like unsubscribe requirements). Using separate sending domains or subdomains for each helps protect transactional email's sender reputation from being harmed by marketing email's inherently higher spam-complaint and bounce rates.
// Transactional: sent from a dedicated subdomain with strict reputation protection
from: 'noreply@transactional.example.com'

// Marketing: sent from a separate subdomain, isolating reputation impact
from: 'newsletter@marketing.example.com'
Real-world example A company separates its password-reset and receipt emails (sent from transactional.example.com) from its weekly newsletter (sent from marketing.example.com), so a spike in spam complaints against the newsletter doesn't damage the sender reputation of critical, time-sensitive transactional emails.

Common follow-ups: What compliance requirements (like CAN-SPAM or GDPR) specifically apply to marketing email that don't apply the same way to transactional email?;How do email providers like SendGrid support separating these two categories at the account or subdomain level?

Security;HTTP & HTTPS Modules

How would you handle email delivery webhooks (bounces, complaints, opens) sent from a provider like SendGrid back to a Node.js application?

Advanced
Email providers send asynchronous webhook events (a message bounced, a recipient marked it as spam, an email was opened) to a URL your application exposes -- the handler should verify the webhook's authenticity (typically via a signature header), process events quickly (often just recording them and returning a fast 200 response, with heavier processing deferred to a background job), and use bounce/complaint data to automatically suppress future sends to addresses that have permanently failed or complained, protecting sender reputation.
app.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySendGridSignature(req)) return res.status(401).end();
  const events = JSON.parse(req.body);
  for (const event of events) {
    if (event.event === 'bounce') suppressList.add(event.email);
  }
  res.status(200).end(); // acknowledge quickly
});
Real-world example A platform automatically adds any email address that generates a hard bounce or spam complaint to an internal suppression list via a SendGrid webhook handler, ensuring future sends never target that address again, protecting the company's sender reputation with mailbox providers over time.

Common follow-ups: Why is verifying the webhook's signature critical before trusting and acting on its payload?;What's the risk of doing heavy processing synchronously inside the webhook handler itself rather than deferring it to a queue?

Security;Background Jobs & Queues

What is an unsubscribe mechanism, and what specific requirements does it need to meet for compliance with regulations like CAN-SPAM?

Intermediate
An unsubscribe mechanism lets recipients opt out of receiving future marketing emails -- CAN-SPAM (in the US) and similar regulations elsewhere require a clear, functioning unsubscribe link in every marketing email, that the opt-out request be honored within a set time period (10 business days under CAN-SPAM), and that the sender not charge a fee or require additional personal information beyond an email address to process the opt-out.
app.get('/unsubscribe', async (req, res) => {
  const { token } = req.query;
  const userId = await verifyUnsubscribeToken(token);
  await updateUserPreferences(userId, { marketingEmailsEnabled: false });
  res.send('You have been unsubscribed successfully.');
});
Real-world example A newsletter system includes a unique, tokenized unsubscribe link in the footer of every marketing email that immediately updates the recipient's preference record upon being clicked, ensuring compliance with the requirement to honor opt-out requests promptly.

Common follow-ups: Why should an unsubscribe link use a signed token rather than just a plain email address in the URL?;Does an unsubscribe request need to also apply to transactional emails, or only marketing emails?

Security;Authentication & Authorization

Showing 1–10 of 15