node.jstypescriptemailnodemailerdreamhost

How to send emails with nodemailer and dreamhost (on Node.js server)


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 🤔


Solution

  • 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.