Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> ซอฟต์แวร์ >> อีเมล

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน

หลายปีมา Quincy Larson ส่งอีเมลนิวส์เลตเตอร์รายสัปดาห์ผ่านแพลตฟอร์ม Mail for Good ของ freeCodeCamp ซึ่งขับเคลื่อนโดย Amazon SES

ล่าสุดเขาย้ายกระบวนการนี้ไปใช้ SendGrid ในบทความนี้ จะแสดงวิธีการสร้างเครื่องมือเพื่อทำสิ่งนี้

วิธีตั้งค่าบัญชี SendGrid

ขั้นแรก สมัคร SendGrid และตั้งค่าบัญชี สำหรับบทช่วยสอนนี้ แพ็กเกจฟรีเพียงพอ

เมื่อขยายแอปพลิเคชัน อาจต้องเพิ่มโควต้าอีเมลผ่านแพลตฟอร์ม

วิธีตั้งค่า Dedicated IP Address บน SendGrid

ค่าเริ่มต้น SendGrid ใช้ IP แบบแชร์สำหรับส่งอีเมล อาจเหมาะสำหรับแอปพลิเคชันขนาดเล็ก แต่เมื่อเพิ่มอัตราการส่ง จะต้องตั้งค่า dedicated IP address

เป็นความคิดที่ดี เพราะ "sender reputation" (เมตริกที่ SendGrid ใช้ประเมินความน่าเชื่อถือกับผู้ให้บริการอีเมล) จะไม่ได้รับผลกระทบเชิงลบจากการกระทำของผู้ใช้คนอื่นที่แชร์ IP เดียวกัน

เพื่อตั้งค่า dedicated IP เอง เลือก "Settings" จากเมนูด้านข้าง แล้วเลือก "IP Addresses" หมายเหตุ: ตัวเลือกนี้ไม่พร้อมใช้งานในแพ็กเกจฟรี

ขึ้นอยู่กับแพ็กเกจที่จ่าย อาจมี dedicated IP อยู่แล้ว หากไม่มี หรือต้องการเพิ่ม ให้เลือกปุ่ม "Add an IP address" เพื่อกำหนดค่า IP ใหม่

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
เมนูตั้งค่าสำหรับที่อยู่ IP

วิธียืนยันผู้ส่งอีเมลใน SendGrid

หมายเหตุ: ข้ามส่วนนี้ได้หากใช้โดเมนส่วนตัวสำหรับอีเมล

เพื่อส่งอีเมลจากอีเมลส่วนตัว ต้องยืนยันว่าอีเมลนั้นเป็นของคุณ

ในเมนูซ้าย เลือก "Settings" แล้ว "Sender Authentication" เลือก "Verify a Single Sender" เพื่อเดินผ่านขั้นตอนเพิ่มอีเมลเดียว

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
ตัวเลือก Single Sender

วิธียืนยันโดเมนส่วนตัวใน SendGrid

หมายเหตุ: ข้ามส่วนนี้ได้หากไม่ใช้โดเมนส่วนตัวสำหรับอีเมล

เพื่อส่งอีเมลจากโดเมนเมลส่วนตัว ต้องยืนยันโดเมนกับ SendGrid ไปที่หน้าจอนี้: เลือกเมนู Settings อีกครั้ง แล้วเลือก "Sender Authentication"

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
เมนูตั้งค่าสำหรับ Sender Authentication

จะเห็นหน้าจอที่มีตัวเลือก "Domain Authentication" เลือก "Authenticate Your Domain" และ SendGrid จะนำคุณผ่านกระบวนการกำหนดค่า DNS records (พร้อมคำแนะนำเฉพาะตาม DNS provider)

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
หน้าตั้งค่า Sender Authentication

วิธีตั้งค่า Reverse DNS ใน SendGrid

หมายเหตุ: ข้ามส่วนนี้ได้หากไม่ใช้โดเมนส่วนตัวสำหรับอีเมล

Reverse DNS (Domain Name System) ใช้โดยผู้ให้บริการอีเมลเพื่อค้นหาเจ้าของ IP address การตั้งค่านี้จะช่วยให้ผู้ให้บริการอีเมลตรวจสอบว่า IP ที่ส่งอีเมลเชื่อมโยงกับโดเมนส่วนตัวของคุณ

ในหน้า Sender Authentication เดียวกัน จะมีส่วน "Reverse DNS" จะมีตัวเลือกกำหนดค่า reverse DNS สำหรับ dedicated IP แต่ละอันในบัญชี - เหมือน Domain Authentication แพลตฟอร์ม SendGrid จะนำคุณผ่านการตั้งค่า

วิธีตั้งค่าการรับรองความถูกต้องของอีเมลใน SendGrid

หมายเหตุ: ข้ามส่วนนี้ได้หากไม่ใช้โดเมนส่วนตัวสำหรับอีเมล

ผู้ให้บริการอีเมลหลัก (Gmail, Yahoo, Outlook) ใช้หลายวิธีรับรองผู้ส่ง: SPF, DKIM, และ DMARC

  • SPF (Sender Policy Framework) ยืนยันว่า IP ที่ส่งเมลจากโดเมนของคุณได้รับอนุญาต
  • DKIM (DomainKeys Identified Mail) ใช้สตริงคีย์สาธารณะรับรองว่า from address ถูกต้องและไม่ถูกปลอม
  • DMARC (Domain-based Message Authentication, Reporting, and Conformance) เป็นชุดคำสั่งบอกผู้ให้บริการอีเมลว่าต้องตอบสนองอย่างไรเมื่ออีเมลล้มเหลวการตรวจสอบ SPF หรือ DKIM

ขั้นตอนการรับรองของ SendGrid จะนำคุณตั้งค่า SPF และ DKIM ระหว่างการยืนยันโดเมน แต่ DMARC ต้องกำหนดค่าเอง

ไปที่ DNS hosting provider และเข้าถึงการจัดการ DNS เพิ่มระเบียน TXT ใหม่ ชื่อ _dmarc.yourdomain.com (แทนที่ yourdomain.com ด้วยโดเมนของคุณ)

หมายเหตุ: บางผู้ให้บริการอย่าง GoDaddy จะเพิ่มโดเมนอัตโนมัติ - ในกรณีนี้ ชื่อควรเป็น _dmarc

ค่า value ของระเบียนควรมีโครงสร้างคล้าย:

"v=DMARC1; p=none; pct=100; rua=mailto:dmarc@yourdomain.com"
  • v=DMARC ระบุเวอร์ชันกฎ DMARC (ปัจจุบันมีเพียง Version 1)
  • p=none ระบุการกระทำเมื่ออีเมลล้มเหลว DKIM หรือ SPF ควรเริ่มที่ none เพื่อไม่ให้กระทบ deliverability หลังยืนยัน DKIM และ SPF ถูกต้องแล้ว สามารถอัปเดตเป็น quarantine (ส่งไปสแปม) หรือ reject (ปฏิเสธ)
  • pct=100 ระบุเปอร์เซ็นต์อีเมลที่ล้มเหลวที่จะนำการกระทำไปใช้
  • rua=mailto:dmarc@yourdomain.com อีเมลสำหรับรับรายงานสรุป แทนที่ด้วยอีเมลที่ต้องการรับรายงาน

วิธีสร้าง Dynamic Template ใน SendGrid

เครื่องมือที่จะสร้างวันนี้ใช้ฟีเจอร์ dynamic template ของ SendGrid เพื่อตั้งค่าหัวข้อและเนื้อหาอีเมล ไปที่ "Email API" ในเมนูด้านข้าง แล้วเลือก "Dynamic Templates"

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
เมนูตั้งค่าสำหรับ Dynamic Templates

จะเห็นหน้าจอพร้อมสร้าง dynamic template แรก เลือก "Create a Dynamic Template"

ตั้งชื่อเทมเพลต: "freeCodeCamp SendGrid Tutorial" SendGrid จะเพิ่มเทมเพลตในรายการ เลือกเทมเพลตเพื่อดู Template ID (จดไว้ จะใช้ในเครื่องมือภายหลัง) และคลิก "Add Version"

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
ตัวอย่างเทมเพลตที่เพิ่งเพิ่ม

เลือก "Blank Template" แล้วเลือก "Code Editor" จะเห็นมุมมองเอดิเตอร์ SendGrid ใช้ HTML สร้างเนื้อหาอีเมล - แต่เครื่องมือของเราจะส่ง plain text

แทนที่เนื้อหาในเอดิเตอร์ด้วยโค้ด:

<p>This is a test email used with the freeCodeCamp SendGrid tutorial</p>
<p>Unsubscribe: {{{unsubscribeId}}}</p>
เครื่องมือของเราส่ง plaintext ดังนั้น HTML boilerplate เพิ่มเติมไม่จำเป็น

สังเกตว่าเราเพิ่ม {{{unsubscribeId}}} SendGrid ใช้ Handlebars แทนค่าแบบไดนามิก - เราจะใช้ประโยชน์จากฟีเจอร์นี้

เลือก settings จากมุมซ้ายบน - อาจตั้งชื่อเวอร์ชันเทมเพลตได้ แต่ฟิลด์ "Subject" คือสิ่งที่ต้องแก้ ตั้งค่าเป็น {{{subject}}} เพื่อโหลดค่าหัวข้อจากเครื่องมือ

เพื่อทดสอบ dynamic template เลือก "Test Data" จากเมนูบน ใส่ JSON:

{ "unsubscribeId": "1", "subject": "Testing emails!" }
จำไว้ว่า JSON ต้องห่อคีย์ด้วยเครื่องหมายคำพูด!

ควรเห็น preview ด้านขวาแสดงค่าเหล่านี้ในเทมเพลต จำไว้กด Save!

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
หน้าจอเอดิเตอร์และ Preview แสดงการโหลดค่าเทมเพลตแบบไดนามิก

วิธีสร้าง API Key ใน SendGrid

ขั้นสุดท้ายในการตั้งค่าบัญชี SendGrid คือ สร้าง API key สำหรับเครื่องมือ

คลิกลูกศรย้อนกลับมุมซ้ายบนกลับหน้าหลัก SendGrid เลือก "Settings" และ "API keys" เลือก "Create API Key" สร้างคีย์ใหม่ อาจให้ "Full Access" แต่สำหรับบทช่วยสอน ต้องการแค่ "Mail Send"

ตั้งชื่อคีย์ให้เข้าใจง่าย เพื่อจำได้ว่าทำไม ถ้าตั้งค่าสิทธิ์เสร็จ เลือก "Create and View" เพื่อสร้างคีย์ - บันทึกไว้ที่ปลอดภ เพราะจะไม่ดูได้อีก

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
หน้าจอสร้าง API พร้อมสิทธิ์ Mail Send

วิธีสร้างเครื่องมือส่งอีเมล

ถึงเวลาเขียนโค้ดส่งอีเมลจริง สามารถดูโค้ดแอปพลิเคชันจริงได้ แต่สำหรับบทช่วยสอน จะสร้างเวอร์ชันลดรูปเพื่อโฟกัสที่ SendGrid API

ซอฟต์แวร์ที่จำเป็นสำหรับสคริปต์แคมเปญอีเมลแบบกำหนดเอง

ต้องมีเครื่องมือต่อไปนี้:

  • Node.js - แนะนำ LTS version
  • IDE เช่น VSCode หรือ Atom

อาจอยากมี git สำหรับควบคุมเวอร์ชัน

เครื่องมือจริงใช้ MongoDB Atlas cluster แต่ตัวอย่างบทช่วยสอนจะไม่ใช้ หากไม่คุ้นเคย MongoDB freeCodeCamp มีหลักสูตรสอนการตั้งค่าและใช้งาน

วิธีเริ่มต้นโปรเจกต์

สร้างโฟลเดอร์ทำงาน เปิดกับเอดิเตอร์และเทอร์มินัล

ตั้งค่าเป็น Node project ด้วย npm init ในเทอร์มินัล จะสร้าง package.json ไฟล์หลักของ Node app

ค่าเริ่มต้นใช้ได้ แต่อาจต้องแก้ส่วน scripts:

 "scripts": {
  "build": "tsc",
  "send": "node ./prod/send.js"
 },
npm init สร้างสคริปต์ test - สามารถลบได้

build ใช้คอมไพล์ TypeScript เป็น JavaScript send ใช้รันแอปพลิเคชัน

ติดตั้งและตั้งค่า TypeScript ถ้ายังไม่คุ้นเคย TypeScript คือ superset ของ JavaScript พร้อม type definitions แข็งแกร่งกว่า และตรวจสอบข้อผิดพลาดขณะคอมไพล์

ติดตั้ง TypeScript: npm install --save-dev typescript (flag --save-dev บันทึกเป็น dev dependency - TypeScript ไม่จำเป็นตอนรันจริง จึงลบได้ใน production)

TypeScript ต้องการไฟล์ config ตัวเอง สร้าง tsconfig.json ใน root directory:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./prod",
    "rootDir": "./src"
  }
}
ถ้าดู repository จะเห็น property excludes เพิ่มเติม สำหรับโครงสร้างไฟล์เฉพาะบทช่วยสอน ไม่จำเป็นต้องมี

ไม่ลึกลงรายละเอียดการตั้งค่า หากต้องการข้อมูลเพิ่ม TypeScript มีเอกสารครบถ้วน

หากใช้ git สร้าง .gitignore ใน root directory ใส่:

/node_modules/
.env
/prod/
  • /node_modules/ ไม่สนใจแพ็กเกจที่ติดตั้ง - best practice สำหรับ version control
  • .env ไม่สนใจไฟล์ environment variables - สำคัญมาก ไม่เคย commit secrets
  • /prod/ ไม่สนใจไฟล์ JavaScript ที่คอมไพล์แล้ว จะใช้โฟลเดอร์นี้สำหรับรายชื่ออีเมล จึงสำคัญที่จะหลีกเลี่ยงการ commit ข้อมูลส่วนตัวโดยบังเอิญ

สร้างไฟล์ .env ใน root project directory โหลด environment variables:

SENDGRID_API_KEY=
SENDGRID_FROM=
SENDGRID_TEMPLATE_ID=
MAIL_SUBJECT=
เวลาใส่ค่า ให้ห่อด้วย double quotes และไม่เว้นวรรครอบ =!
  • SENDGRID_API_KEY คือ API key ที่สร้างข้างต้น
  • SENDGRID_FROM คืออีเมลของคุณ (ใช้สำหรับฟิลด์ from)
  • SENDGRID_TEMPLATE_ID คือสตริง id ของ dynamic template ที่สร้างก่อนหน้า
  • MAIL_SUBJECT จะเป็นหัวข้ออีเมล ตั้งเป็น "fCC Tutorial Email" ไปก่อน

สุดท้าย สร้างโฟลเดอร์ src ใน root และสร้าง send.ts ในโฟลเดอร์นั้น

วิธีติดตั้ง Dependencies

ติดตั้งแพ็กเกจ sendgrid Node.js: npm install @sendgrid/mail แพ็กเกจนี้เป็น wrapper สำหรับ SendGrid API จะช่วยให้เรียก API ส่งอีเมลง่ายขึ้น

ติดตั้ง dev dependencies: npm install --save-dev dotenv @types/node

  • dotenv โหลด environment variables จาก .env ในเครื่อง
  • @types/node ให้ type definitions สำหรับ Node.js - TypeScript ใช้เข้าใจโครงสร้างเมธอดและฟังก์ชัน built-in

วิธีเขียน Logic

ทำงานใน /src/send.ts - ส่วนหลักของแอป เริ่มจาก import ค่าที่ต้องการ

โหลด dotenv และ parse environment variables:

import dotenv from "dotenv";
dotenv.config();
dotenv จำเป็นเฉพาะ local development - โฮสต์ออนไลน์อย่าง Heroku, Repl.it จัดการ env variables เองได้

dotenv.config() อ่าน .env และโหลดค่าเข้า process.env ของ Node

Import โมดูลจาก SendGrid:

import sgMail, { MailDataRequired } from "@sendgrid/mail";
TypeScript import อัตโนมัติได้ แต่อย่างดีควร import เอง

sgMail คือ API wrapper หลัก MailDataRequired คือ type definition ที่ต้องการ

Import ฟีเจอร์ Node built-in สำหรับจัดการไฟล์:

import path from "path";
import { createWriteStream, readFile } from "fs";
Syntax { } ใช้ import โมดูลเฉพาะจากแพ็กเกจ
  • path ใช้หาไฟล์รายชื่ออีเมลด้วย relative paths
  • fs ใช้ read/write ไฟล์เหล่านั้น

เริ่มสร้าง logic! แอปต้องการค่าสำคัญใน .env ต้อง validate ก่อน ถ้าขาด ให้ exit ทันทีเพื่อไม่ให้ error ตอนส่งอีเมล

// ตรวจสอบ API key ที่ถูกต้อง
const apiKey = process.env.SENDGRID_API_KEY;
if (!apiKey) {
  console.error("Missing SendGrid Key");
  process.exit(1);
}

// ตรวจสอบ from address
const fromAddress = process.env.SENDGRID_FROM;
if (!fromAddress) {
  console.error("Missing sender email address!");
  process.exit(1);
}

// ตรวจสอบ dynamic template ID
const sgTemplate = process.env.SENDGRID_TEMPLATE_ID;
if (!sgTemplate) {
  console.error("Missing SendGrid Template ID");
  process.exit(1);
}

// ตรวจสอบ mail subject ถ้าขาดไม่ต้อง exit ใช้ค่า fallback
const subjectValue = process.env.MAIL_SUBJECT || "Fallback value - check your env!";
Syntax || บอกว่า ถ้า process.env.MAIL_SUBJECT เป็น undefined หรือ falsy ใช้สตริงแทน

process.exit(1) บอก Node ให้จบ process ด้วย exit code 1 แสดงว่าแอป crash จากการตรวจสอบล้มเหลว

SendGrid ต้องการตั้งค่า API key เพิ่ม function call ตั้งค่าคีย์:

// ตั้งค่า SendGrid API key
sgMail.setApiKey(apiKey);

ก่อนไปต่อ รัน npm run build - จะสร้างโฟลเดอร์ prod พร้อม JavaScript ที่คอมไพล์แล้ว ควรเห็นโครงสร้างไฟล์:

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
File Tree ณ จุดนี้

ที่จุดนี้ หากใช้ git ให้แน่ใจมากว่า prod folder จะไม่ถูก commit

ในโฟลเดอร์ prod สร้าง validEmails.csv แอปจะอ่านรายชื่ออีเมลจากไฟล์นี้ เริ่มต้นไฟล์ด้วย (แทนที่ your@email.com ด้วยอีเมลจริง):

email,unsubscribeId
your@email.com,1
iama@fake.email,2
.csv ไม่มีเว้นวรรครอบ comma

เขียนโค้ด parse เป็นรายชื่ออีเมล! ใน src/send.ts เพิ่ม:

// รวม file path สำหรับ valid email file
const filePath = path.join(__dirname + "/../validEmails.csv");
// เริ่มอ่านไฟล์!
readFile(filePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data)
});
ใช้ ES6 arrow function syntax ทั้งหมด คุณสามารถใช้ function declarations ได้เช่นกัน

รัน npm run build และ npm run send ควรเห็นเนื้อหา validEmail.csv ในเทอร์มินัล

ตอนนี้ parse string เป็น array of objects เพื่อวนลูปสร้างข้อความอีเมล อัปเดต callback function:

// เริ่มอ่านไฟล์!
readFile(filePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  // parse data เป็น object array
  const emailList = data
    .split("\n")
    .slice(1)
    .map((el) => {
      const [email, unsubscribeId] = el.split(",");
      return { email, unsubscribeId };
    });
});
[email, unsubscribeId] ใช้ destructuring กำหนดค่าจาก split array
  • .split("\n") แยก string ด้วย line breaks หมายเหตุ: บน Windows อาจต้องเปลี่ยน end-of-line setting ของ validEmails.csv จาก CRLF เป็น LF (Windows ใส่ linebreak เพิ่มที่จะกระทบข้อมูล)
  • .slice(1) ลบ element แรก (บรรทัด email,unsubscribeId)
  • map แปลงแต่ละ email,unsubscribeId string เป็น object {email, unsubscribeId}

ผลลัพธ์คือ array of objects พร้อม properties email และ unsubscribeId - ทำงานง่ายกว่า string

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
ตัวอย่าง output จาก parsing function

ถึงเวลาส่งอีเมล เพิ่มโครงสร้าง iteration method (ยังอยู่ใน readFile callback) ใช้ .forEach:

 // วนลูป emailList array
emailList.forEach((user) => {});
พารามิเตอร์ user คือ object {email, unsubscribeId} เรียกว่า user เพราะแทน userdata ของ freeCodeCamp ในแอปเต็มรูปแบบ

ใน callback ของ .forEach สร้าง message object ที่ SendGrid API คาดหวัง:

 // วนลูป emailList array
emailList.forEach((user) => {
  // message object ที่ SendGrid ต้องการ
  const message: MailDataRequired = {
    to: user.email,
    from: fromAddress,
    subject: subjectValue,
    text: "This goes away!",
    templateId: sgTemplate,
    dynamicTemplateData: {
      subject: subjectValue,
      unsubscribeId: user.unsubscribeId
    }
  }
});

ดู message object ใกล้ชิด MailDataRequired ที่ import มาใช้เป็น type definition TypeScript จะเตือนถ้าขาด required property เรามีครบแล้ว แต่แต่ละอย่างหมายถึงอะไร?

  • to: อีเมลปลายทาง - จะเป็น email จากแต่ละบรรทัด validEmails.csv
  • from: อีเมลผู้ส่ง - ตั้งใน .env (ควรเป็นอีเมลของคุณ)
  • subject: ฟิลด์นี้ไม่จำเป็น แต่ให้ fallback หาก dynamic template ไม่ parse subject ได้
  • text: ค่านี้ถูกเขียนทับโดย template แต่สำคัญที่จะใช้ SendGrid ส่งเป็น plaintext หรือ html - ใช้ text แทน html เพื่อให้ template ส่งเป็น plaintext ผู้ให้บริการอีเมล มัก flag HTML ว่า spam ดังนั้นช่วยเพิ่ม deliverability
  • templateId: ID ของ dynamic template ที่ SendGrid ควรใช้
  • dynamicTemplateData: ค่าที่สอดคล้องกับ Handlebars strings ที่ตั้งใน dynamic template

สร้าง message แล้ว ต่อไปส่งมัน เพิ่ม send call (ยังอยู่ใน .forEach callback):

 // ส่ง message ที่สร้างแล้ว!
sgMail.send(message);

จะส่งข้อความไปทุกอีเมลใน validEmails.csv แต่โค้ดรันเงียบๆ ไม่รู้ว่าส่งสำเร็จหรือไม่ เพิ่ม error handling

.send() return Promise ใช้ .then().catch() จัดการ:

 // ส่ง message ที่สร้างแล้ว!
sgMail.send(message)
  .then(() => {
    // log successful sends
    console.info(`Message send success: ${user.email}`)
  })
  .catch((err) => {
    // log failed sends
    console.error(err);
    console.error(`Message send failed: ${user.email}`)
  });
อาจใช้ async/await แต่กรณีนี้ .then.catch ชัดเจนกว่า

รัน npm run build และ npm run send ควรเห็นอีเมลสวยงามใน inbox!

ตอนนี้มีแอปพลิเคชันส่งอีเมลที่ทำงานแล้ว ยินดีด้วย! ดูความคืบหน้าจนถึงที่นี่ได้

อ่านต่อเพื่อดูวิธาจัดการ bounced emails และ logic เพิ่มเติมสำหรับการส่งล้มเหลว

วิธาจัดการ Bounced Emails ใน SendGrid

สังเกตว่า iama@fake.email ไม่ใช่อีเมลจริง SendGrid สร้าง bounced reports รายวันสำหรับกิจกรรมวันก่อน

อีเมลที่ bounce ทุกอันทำลาย reputation ของ SendGrid และอาจทำให้ผู้ให้บริการอีเมล mark เมลของคุณเป็น spam ต้องเพิ่ม logic ป้องกันการส่งไปที่ bouncing addresses ที่รู้จัก

สร้าง bouncedEmails.csv ใน prod folder (ข้าง validEmails.csv) ไม่ต้องมี unsubscribeId เริ่มต้นด้วย:

email
iama@fake.email
เพิ่มอีเมลปลอมเพื่อ示範

กลับไป send.ts บรรทัด 38 ใต้ filePath เดิม เพิ่ม path สำหรับ bouncedEmails.csv:

// รวม file paths สำหรับ CSV files
const filePath = path.join(__dirname + "/validEmails.csv");
const bouncePath = path.join(__dirname + "/bouncedEmails.csv");
__dirname หมายถึง directory ปัจจุบันของไฟล์นี้ (คือ send.ts)

อ่านไฟล์ bounce list เพิ่ม logic อ่านไฟล์ bounced (ก่อน readFile เดิม) เพิ่มทันทีใต้ file path declarations:

// อ่าน bounce list, parse เป็น array
readFile(bouncePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  bounceList = data.split("\n").slice(1);

readFile เป็น asynchronous - ต้อง wrap callback function รอบ ทั้งหมดของ send logic ที่มีอยู่ ย้าย closing }) ของ callback ไปท้ายไฟล์

อ่าน bouncedEmails.csv split โดย new line (จำไว้ว่า line endings ต้องเป็น LF) ลบบรรทัด email แล้วต่อ send logic เดิม

กลับไป send logic ใน .forEach เพิ่ม logic ข้าม blocked emails (ก่อนสร้าง message object):

 // วนลูป emailList array
emailList.forEach((user) => {
  // ตรวจสอบว่าอีเมลเคย bounce หรือไม่
  if (bounceList.length && bounceList.includes(user.email)) {
    console.info(`Message send skipped: ${user.email}`);
    return;
  }
ถ้า bounceList.csv ว่าง เรียก includes จะ error ตรวจ .length ก่อน

ใช้ early return จบ iteration นั้นเมื่อ bounceList มีอีเมลนั้น ป้องกันการพยายามส่งไปที่ bounce แล้ว รัน npm run build และ npm run start ควรเห็น output:

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
ตัวอย่าง console output สำหรับอีเมลที่ข้ามและส่งสำเร็จ

ดูความคืบหน้าจนถึงที่นี่

วิธีจับ Failed Emails ใน SendGrid

ปัจจุบันแอปจะ log error ถ้าส่งล้มเหลว ใช้ได้สำหรับ use cases เล็ก แต่ scale ขึ้น จะยากขึ้นในการระบุ failures และพยายามส่งใหม่

แทนที่จะทำเช่นนั้น ให้แอปบันทึกอีเมลเหล่านั้นในไฟล์ใหม่

สร้าง failedEmails.csv ใน prod folder ไฟล์นี้ว่างได้ เขียนโค้ดเพิ่ม header row

กลับไป send.ts path declarations บรรทัด 38 เพิ่ม path สำหรับ failedEmails.csv:

// รวม file paths สำหรับ CSV files
const filePath = path.join(__dirname + "/validEmails.csv");
const bouncePath = path.join(__dirname + "/bouncedEmails.csv");
const failedPath = path.join(__dirname + "/failedEmails.csv");

ไม่เหมือน path อื่นๆ path นี้ใช้สำหรับ write operation เนื่องจากต้องเขียนต่อเนื่องขณะประมวลผลอีเมล ต้องสร้าง stream สร้าง stream และเพิ่ม header row ใต้ path declarations:

// สร้าง write stream สำหรับ failed emails
const failedStream = createWriteStream(failedPath);
// เพิ่ม header row
failedStream.write("email,unsubscribeId\n")

ปรับปรุง error handling ให้รวม stream ใหม่ เพิ่ม write operation ใน error handling ของ send call:

 // ส่ง message ที่สร้างแล้ว!
sgMail
  .send(message)
  .then(() => {
    // log successful sends
    console.info(`Message send success: ${user.email}`);
  })
  .catch((err) => {
    // log failed sends
    console.error(err);
    console.error(`Message send failed: ${user.email}`);
    // เพิ่มอีเมลนั้นใน failedEmails.csv
    failedStream.write(`${user.email},${user.unsubscribeId}\n`)
  });
เก็บ console statements ไว้เพื่อให้เห็น feedback ขณะสคริปต์รัน

จะเขียน email และ unsubscribeId ลง failedEmails.csv ในรูปแบบที่ถูกต้อง - ทำให้ copy data ไป validEmails.csv เพื่อส่งลองใหม่ได้

ยินดีด้วย! สร้างเครื่องมือส่งอีเมล blast ที่สมบูรณ์และทำงานได้แล้ว ดู completed code ได้ หากต้องการยืนยัน แต่อ่านต่อสำหรับฟีเจอร์ "nice to have" เพิ่มเติม

ฟีเจอร์เพิ่มเติมสำหรับเครื่องมืออีเมล

เพราะเครื่องมือเป็น CLI-based (ใช้ใน command-line interface หรือเทอร์มินัล) ไม่มี user feedback มาก สามารถใช้ console functions เพิ่มเติมเพื่อให้ข้อมูลความคืบหน้า

เพิ่ม "checkpoints" ก่อน validation environment variables พิมพ์ข้อความว่า script เริ่มต้นและกำลังตรวจสอบ:

console.info('Script started. Validating environment variables...')
console.info บ่งบอกว่าเป็นข้อความข้อมูล

หลัง validation พิมพ์ข้อความสำเร็จ:

// ตั้งค่า SendGrid API key
sgMail.setApiKey(apiKey);
console.info('Variables confirmed!')

ใน function อ่าน bounced file เพิ่มข้อความเริ่มต้น ล้มเหลว สำเร็จ:

console.info('Reading bounced email list...')
// อ่าน bounce list, parse เป็น array
readFile(bouncePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    console.error('Failed to read bounced emails!')
    process.exit(1);
  }
  bounceList = data.split("\n").slice(1);
  console.info('Bounced emails read!')

เหมือนกันสำหรับ valid email list:

console.info('Reading send list...')
// เริ่มอ่านไฟล์!
readFile(filePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    console.error('Failed to read send list!')
    return;
  }

อยากมีข้อความ print เมื่อ operation เสร็จ แต่ถ้าเพิ่ม console.info หลัง .forEach loop จะ print ก่อน ที่อีเมลจะส่งเสร็จ!

เพราะ .send สร้าง network call return Promise และ Promise อาจยังไม่ resolve/reject ก่อนที่ iteration จบ

แทนที่จะทำเช่นนั้น สร้าง counter ติดตามจำนวนอีเมลที่ส่ง vs ทั้งหมด เพิ่มตัวแปรก่อน .forEach loop:

 // สร้างตัวแปรนับ
const emailTotal = emailList.length;
let emailCount = 0;
กำหนด .length ให้ตัวแปร หลีกเลี่ยงการอ่านทุก iteration

นับ bounced emails ว่า processed แม้จะข้าม:

 // วนลูป emailList array
emailList.forEach((user) => {
  // ตรวจสอบ bounce
  if (bounceList.includes(user.email)) {
    console.info(`Message send skipped: ${user.email}`);
    emailCount++;
    if (emailCount === emailTotal) {
      console.info(
        `Sending complete! Sent ${emailTotal} emails. Have a nice day!`
      );
      return;
    }
  }

เพิ่ม logic เช็คว่าเป็นอีเมลสุดท้ายใน success และ error handlers:

 // ส่ง message ที่สร้างแล้ว!
sgMail
  .send(message)
  .then(() => {
    // log successful sends
    console.info(`Message send success: ${user.email}`);
    // จัดการ email counts
    emailCount++;
    if (emailCount === emailTotal) {
      console.info(
        `Sending complete! Sent ${emailTotal} emails. Have a nice day!`
      );
    }
  })
  .catch((err) => {
    // log failed sends
    console.error(err);
    console.error(`Message send failed: ${user.email}`);
    // เพิ่มใน failedEmails.csv
    failedStream.write(`${user.email},${user.unsubscribeId}\n`);
    // จัดการ email counts
    emailCount++;
    if (emailCount === emailTotal) {
      console.info(
        `Sending complete! Sent ${emailTotal} emails. Have a nice day!`
      );
    }
  });

แอปสมบูรณ์แล้ว! รัน npm run build และ npm run send ควรเห็น output:

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
ตัวอย่าง console output แอปพลิเคชันเสร็จสมบูรณ์

และควรได้รับอีเมลคล้ายนี้:

ส่งอีเมลนิวส์เลตเตอร์ด้วย SendGrid API: คู่มือขั้นตอนละเอียดครบถ้วน
ตัวอย่างภาพผลลัพธ์อีเมลทดสอบ

ดู final code ได้ หรือดู extended version ที่สร้างสำหรับ freeCodeCamp