Nghe thì có vẻ đơn giản nhưng nghiệp vụ gửi mail lại tương đối phức tạp đến từ sự chậm chạp của quá trình gửi email. Trong bài này chúng ta sẽ tìm hiểu cách để gửi email với NodeJS nhé.

Email làm việc thế nào?

Khi bạn gửi một email cho bạn bè hoặc liên hệ, email gần như được chuyển đến người nhận ngay lập tức trong nháy mắt. Tuy nhiên, có một loạt các quy trình diễn ra ở hậu trường từ khi bạn gửi email cho đến khi email được chuyển đến Hộp thư đến của người nhận. Dưới đây là một tóm tắt ngắn về quy trình này:

  1. Bạn đăng nhập vào email của mình (webmail, thiết bị di động hoặc ứng dụng máy tính để bàn).
  2. Mở trình soạn thảo, chỉ định tiêu đề, nhập nội dung email, chọn người nhận và soạn thảo email.
  3. Nhấn gửi để gửi email.
  4. Ứng dụng email (web, di động, máy tính để bàn) kết nối với máy chủ SMTP gửi đi dựa trên tài khoản email đã sử dụng.
  5. Ứng dụng email chuyển email ở định dạng MIME đến máy chủ SMTP gửi đi.
  6. Máy chủ SMTP gửi đi xác thực chi tiết của người gửi và xử lý thông điệp để gửi.
  7. Kiểm tra kích thước tệp đính kèm và xem email có tuân thủ chính sách gửi đi của tài khoản hay không.
  8. Sau khi xác thực xong, email được đặt vào hàng đợi gửi đi.
  9. Máy chủ SMTP tìm kiếm bản ghi DNS của tên miền và lấy thông tin bản ghi MX của máy chủ nhận (hoặc bản ghi A nếu không có MX).
  10. Máy chủ SMTP kết nối với máy chủ email nhận MTA và gửi email qua giao thức SMTP.
  11. Máy chủ nhận kiểm tra Spam, Virus và chấp nhận email theo chính sách chống Spam, Virus.
  12. Máy chủ nhận xác thực tài khoản người nhận và chuyển email vào tài khoản thư người dùng, theo chính sách email nhận.
  13. Người dùng xem email đã nhận qua ứng dụng email của mình.
    Bạn có thể tham khảo bài viết gốc tại đây.
    Như bạn thấy để gửi một email phải trải qua rất nhiều bước, chính vì thế gửi email rất chậm và đây là một trong những vấn đề không dễ giải quyết.

Khởi tạo dự án

Chúng ta sẽ khởi tạo một thư mục có tên là send-mail và một tập tin main.js.

Gửi một email

Để có thể gửi một email, chúng ta có thể làm đơn giản với mã nguồn kiểu như sau:

const nodemailer = require("nodemailer");

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

async function doSendMail(recipient) {
  const info = await transporter.sendMail({
    from: '"Techmaster 👻" <ta.van.dung@techmaster.vn>',
    to: recipient,
    subject: "Hello ✔",
    text: "Hello world?",
    html: "<b>Hello world?</b>",
  });

  console.log("Message sent: %s", info.messageId);
}

async function main() {
  doSendMail("itprono3@gmail.com");
}

main().catch(console.error);

Ở đây chúng ta đang sử dụng module nodemailer và gửi email thông qua giao thức smtp chúng ta sẽ cần cung cấp:

  1. Host: Là địa chỉ máy chủ SMTP mà bạn sẽ dùng để gửi email.
  2. Port: Là công của máy chủ SMTP.
  3. Secure: Chỉ định xem có sử dụng kết nối bảo mật (SSL/TLS) hay không.
  4. Auth: Là thông tin xác thực đến máy chủ SMTP bao gồm tên đăng nhập và mật khẩu.
    Chúng ta cũng gửi một email thông qua hàm doSendMail với các tham số:
  5. From: Thông tin người gửi.
  6. To: Thông tin người nhận.
  7. Subject: Tiêu đề.
  8. Text: Nội dung văn bản.
  9. Html: Nội dung html.
    Khi chúng ta chạy chương trình bằng lệnh node main.js kết quả chúng ta nhận được sẽ là:
Message sent: <6bb6a6a6-8356-a0ae-f8f3-96a28c7fd5ea@techmaster.vn>

Bạn có thắc mắc tại sao phải dùng async cho phần gửi mail này? Như bạn đã thấy ở phần email làm việc thế nào thì việc gửi email rất chậm dẫn đến việc chúng ta không thể dùng hàm đồng bộ ở đây được để tránh block hàm main.

Gửi nhiều email

Vẫn là nguyên nhân gửi email chậm, nên nếu bạn càng cố gắng gửi càng nhiều email đồng thời thì bộ nhớ sẽ càng bị đầy thêm do các email vẫn phải nằm trong hàng đợi, vậy nên hãy lấy từ từ danh sách email từ cơ sở dữ liệu, gửi theo từng bó, hết bó này đến bó khác để tối ưu bộ nhớ. Mã nguồn ví dụ sẽ như sau:

const nodemailer = require("nodemailer");

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

async function doSendMail(recipient) {
  const info = await transporter.sendMail({
    from: '"Techmaster 👻" <ta.van.dung@techmaster.vn>',
    to: recipient,
    subject: "Hello ✔",
    text: "Hello world?",
    html: "<b>Hello world?</b>",
  });

  console.log("Message sent: %s to %s", info.messageId, recipient);
}

async function sendInBatches(recipients, batchSize) {
  for (let i = 0; i < recipients.length; i += batchSize) {
    const batch = recipients.slice(i, i + batchSize);

    console.log(`Sending batch: ${i / batchSize + 1}`);

    // Chờ tất cả email trong batch hiện tại được gửi
    await Promise.all(batch.map(recipient => doSendMail(recipient)));

    console.log(`Batch ${i / batchSize + 1} sent successfully`);

    // Nghỉ giữa các batch nếu cần, ví dụ nghỉ 5 giây
    await new Promise(resolve => setTimeout(resolve, 5000)); 
  }
}

async function main() {
  const recipients = [
    "itprono3@gmail.com",
    "recipient2@example.com",
    "recipient3@example.com",
    "recipient4@example.com",
    "recipient5@example.com"
  ];

  const batchSize = 2;

  await sendInBatches(recipients, batchSize);
}

main().catch(console.error);

Tổng kết

Như vậy chúng ta đã cùng nhau tạo tìm hiểu và gửi email với nodemailer.


Cám ơn bạn đã quan tâm đến bài viết|video này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a