Email

使用 Nodemailer 发送邮件

Nodemailer 是从 Node.js 服务器发送邮件的标准库。

Nodemailer 是从 Node.js 服务器发送邮件的标准库。你创建一个指向 SMTP 服务器的传输对象,然后使用消息字段调用 sendMail。它负责连接池、附件和 HTML 正文。下面的代码是发送一封真实邮件所需的最少内容,以及大多数人容易忽略的测试技巧。

如何安装和配置 Nodemailer?

从 npm 安装:

npm install nodemailer

传输对象保存你的 SMTP 连接信息。将它指向主机和端口,然后在 auth 下传入凭据:

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
  host: "smtp.example.com",
  port: 587,
  secure: false, // true for port 465, false for 587 (STARTTLS)
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

端口 587 配合 secure: false 使用 STARTTLS,在握手后将连接升级为 TLS。端口 465 需要 secure: true。将用户名和密码保存在环境变量中,切勿写在源代码里。

如何发送消息?

使用信封和内容调用 sendMail。它返回一个 promise,因此将其放在 async/await 中并用 try/catch 包裹,以免发送失败导致进程崩溃:

async function main() {
  try {
    const info = await transporter.sendMail({
      from: '"Your App" <you@yourdomain.com>',
      to: "recipient@example.org",
      subject: "Your receipt",
      text: "Thanks for your order. This is the plain-text version.",
      html: "<p>Thanks for your order.</p>",
    });
    console.log("Sent:", info.messageId);
  } catch (err) {
    console.error("Send failed:", err);
  }
}

main();

始终同时发送 texthtml。部分客户端会渲染纯文本部分,而没有纯文本备选内容的邮件往往在垃圾邮件过滤器中得分更低。

如何添加附件?

传入一个 attachments 数组。每个条目可以从路径、buffer 或 stream 读取:

await transporter.sendMail({
  from: "you@yourdomain.com",
  to: "recipient@example.org",
  subject: "Your invoice",
  text: "Invoice attached.",
  attachments: [
    {
      filename: "invoice.pdf",
      path: "./invoices/invoice-1042.pdf",
    },
    {
      filename: "note.txt",
      content: "Generated at runtime, no file needed.",
    },
  ],
});

如何在不发送真实邮件的情况下测试?

Nodemailer 内置了 Ethereal,一个伪造的 SMTP 服务,它捕获消息而不实际投递。每次发送都会生成一个预览 URL,非常适合本地开发:

const testAccount = await nodemailer.createTestAccount();

const transporter = nodemailer.createTransport({
  host: "smtp.ethereal.email",
  port: 587,
  secure: false,
  auth: {
    user: testAccount.user,
    pass: testAccount.pass,
  },
});

const info = await transporter.sendMail({
  from: "you@yourdomain.com",
  to: "test@example.org",
  subject: "Preview only",
  text: "This never leaves Ethereal.",
});

console.log("Preview:", nodemailer.getTestMessageUrl(info));

Ethereal 用于检查你构建的内容,它无法告诉你真实收件箱是否会接受该消息。

使用 Bird 发送

SMTP 可以工作,但在生产环境中运行意味着你要自行运维邮件服务器(或租用一台)、预热 IP 以及监控信誉。HTTP API 可以替你处理这些。不需要保持 SMTP 套接字连接,提供商会管理投递基础设施。使用 Bird 邮件 API,同样的发送只需一次调用:

import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });

const { data, error } = await bird.email
  .send({
    from: "you@yourdomain.com",
    to: ["delivered@bird.dev"],
    subject: "Hello from Node",
    html: "<p>It works.</p>",
  })
  .safe();

delivered@bird.dev 沙盒地址始终接受邮件,因此你可以在指向真实收件人之前确认连接是否正常。Serverless 平台是这种方式最典型的适用场景,因为其函数运行时通常根本无法保持 SMTP 长连接。参阅如何在 Vercel 上发送邮件了解该路径,以及更全面的使用 JavaScript 发送邮件概览,了解邮件代码在你的技术栈中应放在何处。

常见问题

Nodemailer 能在浏览器中运行吗?

不能。Nodemailer 是服务端库,依赖 Node 的网络模块。浏览器无法打开 SMTP 连接,而且将凭据放在客户端代码中会暴露它们。请改从后端发送。

为什么要同时发送纯文本和 HTML?

多部分消息让每个客户端选择它能渲染的部分,纯文本备选内容有助于垃圾邮件过滤器解析邮件。省略 text邮件进入垃圾箱的常见原因。

我应该使用 SMTP 还是 HTTP API?

SMTP 适合低发送量和内部工具。对于大规模生产发送,HTTP API 免去了你需要运维的服务器,并内置了送达能力管理。许多团队在开发环境使用 Nodemailer,在生产环境使用 API。

Nodemailer 是从 Node 通过 SMTP 发送邮件的可靠选择。如果你不想自行运维背后的邮件服务器,请阅读邮件发送指南,了解 API 替代方案的完整流程。

基于同一网络构建。

测试 API 密钥即刻获取。添加付款方式并验证发送者身份后,即可解锁生产环境。

你的下一个创意。
随时连接。