十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Wasp 邮件发送指南:emailSender 配置、四大 Provider 接入与 SDK 源码剖析

Wasp 邮件发送指南:emailSender 配置、四大 Provider 接入与 SDK 源码剖析 Wasp 邮件发送指南emailSender 配置、四大 Provider 接入与 SDK 源码剖析【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/waspWasp 将发送邮件封装成了开箱即用的内置能力只需在 Wasp 声明文件中配置emailSender即可在服务端代码里通过wasp/server/email模块的emailSender.send()一行发送带文本与 HTML 内容的邮件并支持 Dummy、SMTP、Mailgun、SendGrid 四种 Provider 的即插即用。本文以 version-0.12 版本文档为主线结合仓库中 SDK 模板的源码实现完整讲解配置方法、环境变量、API 字段语义与底层调用链帮助你零重复代码地把邮件功能集成进 Wasp 应用。Wasp 的邮件能力从何而来Wasp 是一个声明式全栈框架像邮件发送这类横跨配置、依赖、鉴权与部署的复杂功能被抽象成一个声明字段再由框架生成对应的 SDK 代码。在 version-0.12 中你只需要在main.wasp文件里给app声明一个emailSender字典app Example { ... emailSender: { provider: provider, defaultFrom: { name: Example, email: helloitsme.com }, } }可选 Provider 一共有四种Dummy仅用于开发调试MailgunSendGrid以及经典可靠的SMTP。defaultFrom是可选项一旦声明了默认发件人之后每次send()时就不必再传from字段。提示当前仓库主线的 web/docs/advanced/email.md 已演进为main.wasp.ts基于wasp.sh/spec的 TypeScript 声明写法并新增了ResendProvider本文以 version-0.12 的main.waspDSL 为骨架会在对应小节中补充主线新增内容。发送第一封邮件emailSender.send()配置好 Provider 后发送邮件只需两步从wasp/server/email导入emailSender然后调用其send方法。下面是在某个 Action 处理器中发送邮件的完整示例import { emailSender } from wasp/server/email; // In some action handler... const info await emailSender.send({ from: { name: John Doe, email: johndoe.com, }, to: userdomain.com, subject: Saying hello, text: Hello world, html: Hello strongworld/strong, });如果是 JavaScript 项目写法完全一致import { emailSender } from wasp/server/email;只是文件扩展名为.js。send返回一个对象描述邮件的发送状态。需要特别注意的是该返回值的结构随 Provider 不同而变化——SMTP 走的是 nodemailer 的sendMail结果Mailgun 返回其 messages API 的响应SendGrid 返回其 HTTP 响应。因此业务代码不应强依赖某一 Provider 的返回结构。send 方法的字段语义send方法接收的对象包含以下字段字段类型必填说明fromobject否*发件人信息含name: string发件人显示名与email: string发件人邮箱。若已在emailSender中配置defaultFrom则可省略tostring是收件人邮箱地址subjectstring是邮件主题textstring是邮件的纯文本版本htmlstring是邮件的 HTML 版本*from是否必填由defaultFrom是否声明决定这一约束在生成的 SDK 类型里被精确表达详见下文类型层面的默认值约束。从 SDK 模板源码 email/core/types.ts 可以看到Email类型的完整定义export type Email { // 只有配置了 defaultFrom 时from 才是可选字段 from?: EmailFromField; // 或 from: EmailFromField to: string; subject: string; text: string; html: string; }; export type EmailFromField { name?: string; email: string; };四大 Provider 接入详解配置 Provider 的核心是两件事在main.wasp里指定provider名称再把对应的环境变量写入.env.server文件服务端环境变量文件。Dummy Provider开发期在控制台打印邮件为了加快开发节奏Wasp 内置了一个Dummy邮件发送器它不会真实发送邮件而是把邮件内容console.log到服务端控制台因此完全不需要任何配置。app Example { ... emailSender: { provider: Dummy, } }注意Dummy Provider 仅限开发环境使用。它不适用于生产环境——如果你尝试用DummyProvider 构建build应用构建会直接失败从机制上防止把调试用 Provider 带上线。从源码看Dummy 实现位于 email/core/providers/dummy.ts它用黄色 ANSI 颜色在控制台打印一个邮件信封export function initDummyEmailSender(_config?: DummyEmailProvider): EmailSender { const defaultFromField getDefaultFromField(); return { send: async (email) { const fromField email.from || defaultFromField; console.log(colorize(yellow, ╔═══════════════════════╗)); console.log(colorize(yellow, ║ Dummy email sender ✉️ ║)); console.log(colorize(yellow, ╚═══════════════════════╝)); console.log(From: ${fromField.name} ${fromField.email}); console.log(To: ${email.to}); console.log(Subject: ${email.subject}); // ... 分别打印 text 与 html 内容 return { success: true }; }, }; }这让你在本地开发时能直观看到每次发送的收件人、主题、纯文本与 HTML 内容非常适合联调邮件模板与邮件触发逻辑。SMTP Provider接入任意 SMTP 服务把 Provider 设为SMTPapp Example { ... emailSender: { provider: SMTP, } }然后在.env.server文件中配置以下环境变量SMTP_HOST SMTP_USERNAME SMTP_PASSWORD SMTP_PORTSMTP 的兼容性极广许多事务邮件服务商如 Mailgun、SendGrid以及其他厂商也提供 SMTP 接入方式因此如果你暂时不想使用各家专属 SDK完全可以把它们的 SMTP 端点填进上面的配置里。注意事项部分云主机会封锁 SMTP 出站端口。一些托管平台例如 Railway 的免费套餐、Hetzner出于反垃圾邮件考虑会封锁 25/465/587 等 SMTP 出站端口。如果遇到发送失败应先查阅托管商的文档寻找解决方案或改用 Mailgun、SendGrid 等专用 Provider 集成。从源码看SMTP 实现位于 email/core/providers/smtp.ts底层使用 nodemailer 的createTransport创建传输通道import { createTransport } from nodemailer; export function initSmtpEmailSender(config: SMTPEmailProvider): EmailSender { const transporter createTransport({ host: config.host, port: config.port, auth: { user: config.username, pass: config.password }, }); const defaultFromField getDefaultFromField(); return { async send(email) { return transporter.sendMail({ from: formatFromField(email.from || defaultFromField), to: email.to, subject: email.subject, text: email.text, html: email.html, }); }, }; }注意formatFromField会把{ name, email }格式化成Name emailexample.com的标准邮件头格式无name时退化为纯邮箱地址这一辅助函数定义在 email/core/helpers.ts。Mailgun ProviderAPI Key Domain把 Provider 设为Mailgunapp Example { ... emailSender: { provider: Mailgun, } }接着获取 Mailgun 的 API Key 与域名并写入.env.serverMAILGUN_API_KEY MAILGUN_DOMAIN获取 API Key 与 Domain 的步骤前往 Mailgun 官网注册账号在 API Keys 页面创建新的 API Key将 API Key 复制到.env.server文件的MAILGUN_API_KEY在 Domains 页面创建新域名将域名复制到.env.server文件的MAILGUN_DOMAIN。欧盟区域EU Region补充说明如果你的 Mailgun 域名区域位于欧盟还需额外设置MAILGUN_API_URL环境变量指向欧盟 API 端点如https://api.eu.mailgun.net。这一点在 SDK 模板 email/core/types.ts 中体现为可选的apiUrl字段。从源码看Mailgun 实现位于 email/core/providers/mailgun.ts使用mailgun.js客户端发起messages.createimport Mailgun from mailgun.js; export function initMailgunEmailSender(config: MailgunEmailProvider): EmailSender { const defaultFromField getDefaultFromField(); const mailgun new Mailgun(FormData); const mailer mailgun.client({ username: api, key: config.apiKey, url: config.apiUrl, // 可选EU 区域需要 }); return { async send(email) { const fromField email.from || defaultFromField; return mailer.messages.create(config.domain, { from: ${fromField.name} ${fromField.email}, to: [email.to], subject: email.subject, text: email.text, html: email.html, }); }, }; }SendGrid Provider一个 API Key 搞定把 Provider 设为SendGridapp Example { ... emailSender: { provider: SendGrid, } }然后获取 SendGrid 的 API Key 并写入.env.serverSENDGRID_API_KEY获取 API Key 的步骤前往 SendGrid 官网注册账号在 API Keys 页面创建新的 API Key将 API Key 复制到.env.server文件的SENDGRID_API_KEY。从源码看SendGrid 实现位于 email/core/providers/sendgrid.ts底层使用sendgrid/mail官方库并针对其错误数组隐藏在响应体内的行为做了兜底处理——当响应体包含错误数组时会抛出携带全部错误的AggregateError方便定位问题import SendGrid from sendgrid/mail; export function initSendGridEmailSender(provider: SendGridProvider): EmailSender { SendGrid.setApiKey(provider.apiKey); const defaultFromField getDefaultFromField(); return { async send(email) { const fromField email.from || defaultFromField; return SendGrid.send({ from: { email: fromField.email, name: fromField.name }, to: email.to, subject: email.subject, text: email.text, html: email.html, }).catch((error) { const responseErrors error?.response?.body?.errors; if (responseErrors Array.isArray(responseErrors)) { throw new AggregateError([...responseErrors, error], SendGrid error: ${error.message}); } throw error; }); }, }; }时间提示SendGrid 已于 2025 年 5 月 27 日起停止提供免费套餐发送邮件需要付费计划需要免费额度时可考虑 Mailgun 或其他支持 SMTP 的服务。当前主线文档 web/docs/advanced/email.md 中还新增了ResendProviderRESEND_API_KEY环境变量并同步在 SDK 模板中提供resend.ts实现。源码视角emailSender 是如何被生成出来的Wasp 声明文件中的emailSender并不直接参与运行时逻辑而是驱动代码生成器产出对应的 SDK 模块。整个链条如下生成 SDK 入口模板 sdk/wasp/server/email/index.ts 根据你在 Wasp 文件中选择的 Provider生成对应的emailProvider配置对象并导出统一入口emailSender// 以 SMTP 为例生成后 const emailProvider { type: smtp, host: env.SMTP_HOST, port: env.SMTP_PORT, username: env.SMTP_USERNAME, password: env.SMTP_PASSWORD, } as const; export const emailSender: EmailSender initEmailSender(emailProvider); export type { Email, EmailFromField, EmailSender, SentMessageInfo } from ./core/types.js;选择 Provider 实现模板 sdk/wasp/server/email/core/index.ts 用条件编译的方式把initEmailSender绑定到对应 Provider 的初始化函数initSmtpEmailSender/initSendGridEmailSender/initMailgunEmailSender/initDummyEmailSender。这意味着未被选中的 Provider 代码不会进入你的构建产物包体积和攻击面都得到控制。统一对外契约无论底层是 nodemailer、mailgun.js 还是 SendGrid SDK对外暴露的都是同一个EmailSender接口send: (email: Email) PromiseSentMessageInfo你的业务代码因此与具体邮件服务商解耦日后切换 Provider 只需改声明文件与环境变量。环境变量的接入Provider 所需的SMTP_HOST、SMTP_PORT、MAILGUN_API_KEY、MAILGUN_DOMAIN、MAILGUN_API_URL、SENDGRID_API_KEY等均从wasp/server/env读取见模板 sdk/wasp/server/env.ts对应.env.server文件中的键名。类型层面的默认值约束from 字段的条件可选这是 Wasp 代码生成的一大亮点defaultFrom是否声明会直接改变生成的 TypeScript 类型。在 email/core/types.ts 中若你在emailSender中配置了defaultFrom生成的Email类型里from是可选字段from?: EmailFromField若未配置from则是必填字段from: EmailFromField。配合 email/core/helpers.ts 中的getDefaultFromField()将defaultFrom硬编码进生成代码send内部通过email.from || defaultFromField完成发件人兜底。于是配置了默认发件人后每次发送都可省略 from这一规则在编译期就被类型系统强制执行而不是留到运行时才发现。实战建议与常见坑开发期请善用 Dummy Provider在本地开发、联调邮件模板时使用Dummy既不需要任何真实账号又能完整看到邮件的 text/html 内容但要牢记它是非生产组件构建期会被拒绝。Provider 返回值不可通用send()的返回结构因 Provider 而异业务逻辑应把它当作发送结果而非强类型契约来使用真正需要追踪送达情况时应依赖各邮件服务商后台的日志与事件回调。SMTP 端口可能被托管商封锁Railway免费套餐、Hetzner 等平台会封锁出站 SMTP 端口遇到发送失败先排查端口连通性或改用 Mailgun/SendGrid 等专用集成。与认证邮件联动Wasp 的邮箱验证、密码重置等认证邮件同样构建在emailSender之上相关声明与钩子如邮箱验证后的回调可参考 认证邮件文档两者共用同一套 Provider 配置。声明文件的版本差异version-0.12 使用main.waspDSL 语法app Example { ... }当前主线已迁移到main.wasp.tsexport default app({ ... })迁移细节可对照当前版本文档 web/docs/advanced/email.md 与 Wasp TypeScript 声明说明。小结Wasp 把发送邮件沉淀为一条清晰的声明式链路main.wasp中声明 Provider 与默认发件人 → 在.env.server中填入对应环境变量 → 服务端代码统一调用wasp/server/email的emailSender.send()。四种 ProviderDummy / SMTP / Mailgun / SendGrid各有适用场景而生成器在类型层面为你强制校验from字段的可选性让零配置接入、零重复代码落到实处。若想深入源码可以从 SDK 模板目录 waspc/data/Generator/templates/sdk/wasp/server/email/ 开始按index.ts→core/index.ts→core/providers/*.ts的顺序完整追踪一条 Provider 的生成与运行链路。【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表