Decoupling Node.js Email Delivery with a Durable SMTP Queue
A campaign approval, balance change, or account-security alert should not make a Node.js request wait on an email server. If the relay is slow, the user-facing action can time out; if the app retries
On this page0%
A campaign approval, balance change, or account-security alert should not make a Node.js request wait on an email server. If the relay is slow, the user-facing action can time out; if the app retries the request, it may send the same message twice. A safer production setup is to record the notification alongside the business event, then let a background worker deliver it over SMTP.
Move email out of the request path
For a crypto ad network, consider a campaign that changes from pending to approved. The application should commit that state change promptly. It should not depend on an SMTP connection completing before it can return a response to the advertiser.
Store an email job in a database table or durable queue as part of the same transaction as the state change. A worker reads pending jobs and sends them through the relay. This separates application availability from email delivery: a temporary relay outage can delay the notification without undoing an approval or blocking a dashboard action.
If you are introducing smtp relay nodejs into an existing service, treat the worker boundary as the main design decision, not the mail library. The Email & messaging platform can provide the SMTP connection for transactional messages, but your application still needs to decide when a message is created, how retries work, and what happens when delivery is uncertain.
Give each business event one notification job
Create the job when the underlying event is committed, rather than generating it whenever a page loads or a user retries an API request. Give the job a stable event key, such as the campaign identifier plus the state transition and version. Enforce uniqueness on that key so duplicate requests do not create multiple pending approval emails.
A useful job record contains the recipient, a template identifier, the minimal data needed to render the message, an attempt count, a next-attempt time, and a status. Avoid storing SMTP credentials or unnecessary account information in it. If the recipient can change before sending, decide whether the job should use the address captured at event time or the account’s current verified address; make that policy explicit.
Keep the rendered email tied to a real state change. A worker should not infer approval from a stale queue payload. Before sending a sensitive alert, it can verify the current event version or load the relevant record. This avoids telling a customer that a campaign was approved if it was paused immediately afterward.
Configure the Node.js relay in the worker
Use a maintained SMTP client such as Nodemailer, and read connection details from the worker’s environment rather than committing them to source control. The following example uses the common submission port; adjust the port and TLS settings to match the credentials supplied for your account.
const nodemailer = require("nodemailer");
const port = Number(process.env.SMTP_PORT || 587);
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port,
secure: port === 465,
requireTLS: port !== 465,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
async function sendCampaignNotice(message) {
return transporter.sendMail({
from: process.env.MAIL_FROM,
to: message.to,
subject: message.subject,
text: message.text,
html: message.html
});
}
Set the sender to an address approved for your sending setup, and use a distinct credential for the worker rather than reusing an administrator login. Do not print the complete message, password, or authentication response in logs. A startup verification can catch invalid settings, but it does not prove that a particular recipient will accept a later message.
Retry temporary failures without creating a mail storm
Classify failures instead of retrying every error at the same pace. A temporary connection failure or a server response indicating that the service is busy may be retried with increasing delays. Invalid credentials, malformed recipients, and other persistent configuration or address errors need investigation, not rapid retries. Set a maximum attempt count and move exhausted jobs into a state that an operator can inspect.
Use a worker lease or equivalent claim mechanism so two Node.js processes cannot send the same pending job at once. When a worker claims a job, record a lease expiration; if the process dies, another worker can reclaim it after the lease expires. Keep the lease longer than the normal send timeout, and extend it if message rendering or delivery can take longer.
SMTP cannot provide a true exactly-once guarantee for your application. The relay may accept a message and the connection may fail before your worker receives confirmation. Retrying in that case can produce a duplicate; marking it sent before acceptance can instead lose the message. Use stable event keys to prevent duplicate job creation, keep retry behavior bounded, and accept that this narrow uncertain outcome cannot always be resolved automatically.
Deploy the change in a controlled sequence
Before switching production traffic, send test jobs from a non-production environment and confirm the worker can authenticate, render the expected content, and record success and failure states. Then enable the worker for one low-risk notification type. Compare the number of business events, queued jobs, and completed sends; a growing backlog can reveal a worker or configuration problem before you move critical alerts.
- Keep the web application and worker on the same message schema during rollout.
- Make job creation idempotent before running multiple worker instances.
- Monitor queue age, retry counts, permanent failures, and worker health.
- Keep a manual replay path for reviewed failures, rather than resending every failed job blindly.
Once the worker is stable, remove direct SMTP sends from request handlers so there is one controlled path for these messages. If you use the Email & messaging platform as the relay, it handles the SMTP handoff; your queue, event rules, retry policy, and operational review remain your responsibility. This division is useful when account events must be recorded quickly even during brief email interruptions, but it does not replace monitoring or a clear policy for delayed notifications.