I can't find any helpful posts specific to sending emails from dreamhost using nodemailer. I did find a dreamhost article for sending emails via PHP so maybe that can help point us in the right direction...
Either way here's my mailer.ts
file:
import { createTransport, getTestMessageUrl } from "nodemailer";
import { frontendURL } from "./urls";
var transport = createTransport({
host: "smtp.dreamhost.com",
port: 465,
auth: {
user: encodeURIComponent(process.env.PROD_MAIL_USER),
pass: encodeURIComponent(process.env.PROD_MAIL_PASS),
},
secure: true,
});
function generateHTML(resetToken: string) {
return `
<div>
<h2>Hello from HaBits 👋</h2>
<p>
Your <b>Password Reset Token</b> is here!
</p>
<h3>
<a href="${frontendURL}/reset?token=${resetToken}" style="color: #75B748">Reset your password.</a>
</h3>
<p>HaBits ✅</p>
</div>
`;
}
export interface MailResponse {
accepted?: string[] | null;
rejected?: null[] | null;
envelopeTime: number;
messageTime: number;
messageSize: number;
response: string;
envelope: Envelope;
messageId: string;
}
export interface Envelope {
from: string;
to?: string[] | null;
}
export async function sendPasswordResetEmail(
resetToken: string,
to: string
): Promise<void> {
// email the user a token
const info = await transport.sendMail({
to,
from: process.env.PROD_MAIL_USER,
subject: "✅ HaBits – password reset token",
html: generateHTML(resetToken),
});
}
Error response in production:
{
"errors": [
{
"message": "Invalid login: 535 5.7.8 Error: authentication failed: ",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"sendUserPasswordResetLink"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"code": "EAUTH",
"response": "535 5.7.8 Error: authentication failed: ",
"responseCode": 535,
"command": "AUTH PLAIN",
"stacktrace": [
"Error: Invalid login: 535 5.7.8 Error: authentication failed: ",
" at SMTPConnection._formatError (/workspace/backend/node_modules/nodemailer/lib/smtp-connection/index.js:784:19)",
" at SMTPConnection._actionAUTHComplete (/workspace/backend/node_modules/nodemailer/lib/smtp-connection/index.js:1536:34)",
" at SMTPConnection.<anonymous> (/workspace/backend/node_modules/nodemailer/lib/smtp-connection/index.js:540:26)",
" at SMTPConnection._processResponse (/workspace/backend/node_modules/nodemailer/lib/smtp-connection/index.js:947:20)",
" at SMTPConnection._onData (/workspace/backend/node_modules/nodemailer/lib/smtp-connection/index.js:749:14)",
" at TLSSocket.SMTPConnection._onSocketData (/workspace/backend/node_modules/nodemailer/lib/smtp-connection/index.js:189:44)",
" at TLSSocket.emit (node:events:390:28)",
" at addChunk (node:internal/streams/readable:315:12)",
" at readableAddChunk (node:internal/streams/readable:289:9)",
" at TLSSocket.Readable.push (node:internal/streams/readable:228:10)",
" at TLSWrap.onStreamRead (node:internal/stream_base_commons:199:23)"
]
}
},
"name": "GraphQLError"
}
]
}
Dreamhost "support" just sent me the article for PHP which suggests using host: "ssl://smtp.dreamhost.com"
but that just returns a less helpful "can't find address" error:
Error: getaddrinfo ENOTFOUND ssl://smtp.dreamhost.com
So I'm fairly confident the nodemailer config is correct – ie. host, port, user and password 🤔
This was my own silly mistake 🤦♂️
Here's the nodemailer config to send emails in production (assuming you have an email hosted by dreamhost):
import { createTransport, getTestMessageUrl } from "nodemailer";
var transport = createTransport({
host: "smtp.dreamhost.com",
port: 465,
auth: {
user: process.env.PROD_MAIL_USER, // your email address
pass: process.env.PROD_MAIL_PASS, // your webmail password
},
secure: true,
logger: true,
debug: true,
});
// ...
await transport.sendMail({ ... })
My issue was the encodeURIComponent()
wrapped around my email and password – so email hello@example.com
was being passed as hello%40example.com
.
The logger
and debugger
properties above are optional but they helped me identify the issue in my server logs.