Email/

Sending Emails With Nodemailer

Nodemailer is the standard library for sending email from a Node.js server. You create a transport pointed at an SMTP server, then call sendMail with the message fields. It handles connection pooling, attachments, and HTML bodies. The code below is the minimum you need to send a real message, plus the testing trick most people miss.

How do you install and configure Nodemailer?

Install it from npm:

npm install nodemailer

A transport holds your SMTP connection details. You point it at a host and port, then pass credentials under 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,
  },
});

Port 587 with secure: false uses STARTTLS, which upgrades the connection to TLS after the handshake. Port 465 wants secure: true. Keep the username and password in environment variables, never in the source.

How do you send a message?

Call sendMail with the envelope and content. It returns a promise, so wrap it in async/await with a try/catch so a failed send does not crash the process:

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();

Always send both text and html. Some clients render the plain-text part, and a message with no text alternative tends to score worse with spam filters.

How do you add attachments?

Pass an attachments array. Each entry can read from a path, a buffer, or a 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.",
    },
  ],
});

How do you test without sending real mail?

Nodemailer ships with Ethereal, a fake SMTP service that captures messages instead of delivering them. You get a preview URL for each send, which is ideal for local development:

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 is for inspecting what you built. It tells you nothing about whether a real inbox would accept the message.

Send it with Bird

SMTP works, but running it in production means operating a mail server (or renting one), warming IPs, and watching reputation yourself. An HTTP API hands that off. There is no SMTP socket to keep open, and the provider manages deliverability infrastructure. With the Bird email API the same send is a single call:

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();

The delivered@bird.dev sandbox address always accepts mail, so you can confirm the wiring before you point at a real recipient. Serverless platforms are the clearest case for this approach, since their function runtimes often cannot hold open SMTP connections at all. See how to send email on Vercel for that path, and the broader sending email with JavaScript overview for where email code belongs in your stack.

FAQ

Does Nodemailer work in the browser?

No. Nodemailer is a server-side library and depends on Node's networking modules. Browsers cannot open SMTP connections, and putting credentials in client code would expose them. Send from a backend instead.

Why send both text and HTML?

A multipart message lets each client pick the part it can render, and the plain-text alternative improves how spam filters read the message. Skipping text is a common reason for emails landing in spam.

Should I use SMTP or an HTTP API?

SMTP is fine for low volume and internal tools. For production sending at scale, an HTTP API removes the server you would otherwise operate and folds in deliverability handling. Many teams use Nodemailer in development and an API in production.

Nodemailer is a solid choice for talking SMTP from Node. When you would rather not run the mail server behind it, read the sending email guide to see the API alternative end to end.

从一个渠道开始。
准备好后,再添加其他渠道。

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

正在使用 Claude Code、Cursor 或 Codex?复制一条设置提示,您的智能代理即可自动安装 Bird CLI 和相关技能。选择您的工具:

Cursor