next-forge邮件服务:Nodemailer与SendGrid集成指南
邮件服务架构概览
next-forge作为生产级Next.js应用模板,其邮件服务模块采用分层架构设计,核心基于React Email组件系统构建,支持多邮件服务商无缝切换。以下是邮件服务的核心组件关系:
邮件服务模块位于packages/email目录,提供统一的邮件发送接口,同时在apps/email目录下提供独立的邮件开发环境,支持模板预览和调试。
核心依赖与配置体系
依赖组件分析
next-forge邮件服务的核心依赖如下表所示:
| 依赖包 | 版本 | 作用 |
|---|---|---|
| @react-email/components | 0.0.41 | 提供邮件UI组件库 |
| @t3-oss/env-nextjs | ^0.13.4 | 环境变量管理 |
| resend | ^4.5.1 | 默认邮件发送服务 |
| react | ^19.1.0 | 模板渲染基础 |
| zod | ^3.25.28 | 类型验证 |
环境配置系统
邮件服务的配置通过keys.ts实现类型安全的环境变量管理:
// packages/email/keys.ts
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';
export const keys = () =>
createEnv({
server: {
RESEND_FROM: z.string().email(),
RESEND_TOKEN: z.string().startsWith('re_'),
// Nodemailer配置
SMTP_HOST: z.string().optional(),
SMTP_PORT: z.coerce.number().optional(),
SMTP_USER: z.string().optional(),
SMTP_PASSWORD: z.string().optional(),
// SendGrid配置
SENDGRID_API_KEY: z.string().optional(),
},
runtimeEnv: {
RESEND_FROM: process.env.RESEND_FROM,
RESEND_TOKEN: process.env.RESEND_TOKEN,
SMTP_HOST: process.env.SMTP_HOST,
SMTP_PORT: process.env.SMTP_PORT,
SMTP_USER: process.env.SMTP_USER,
SMTP_PASSWORD: process.env.SMTP_PASSWORD,
SENDGRID_API_KEY: process.env.SENDGRID_API_KEY,
},
});
默认Resend实现
next-forge默认集成Resend作为邮件发送服务,实现位于packages/email/index.ts:
// packages/email/index.ts
import { Resend } from 'resend';
import { keys } from './keys';
export const resend = new Resend(keys().RESEND_TOKEN);
// 邮件发送函数
export async function sendEmail({
to,
subject,
react,
}: {
to: string | string[];
subject: string;
react: React.ReactElement;
}) {
const { data, error } = await resend.emails.send({
from: keys().RESEND_FROM,
to,
subject,
react,
});
if (error) {
console.error('Email sending failed:', error);
throw new Error(`Email error: ${error.message}`);
}
return data;
}
邮件模板开发
邮件模板使用React Email组件开发,位于packages/email/templates目录。以contact.tsx为例:
// packages/email/templates/contact.tsx
import {
Body,
Container,
Head,
Hr,
Html,
Preview,
Section,
Tailwind,
Text,
} from '@react-email/components';
type ContactTemplateProps = {
readonly name: string;
readonly email: string;
readonly message: string;
};
export const ContactTemplate = ({
name,
email,
message,
}: ContactTemplateProps) => (
<Tailwind>
<Html>
<Head />
<Preview>New email from {name}</Preview>
<Body className="bg-zinc-50 font-sans">
<Container className="mx-auto py-12">
{/* 邮件内容 */}
</Container>
</Body>
</Html>
</Tailwind>
);
开发环境可通过apps/email启动独立预览服务器:
cd GitHub_Trending/ne/next-forge
pnpm --filter @repo/email dev
Nodemailer集成指南
安装依赖
pnpm add nodemailer @types/nodemailer
创建Nodemailer适配器
// packages/email/adapters/nodemailer.ts
import { createTransport } from 'nodemailer';
import { keys } from '../keys';
import ReactDOMServer from 'react-dom/server';
const config = keys();
export const nodemailerTransport = createTransport({
host: config.SMTP_HOST,
port: config.SMTP_PORT,
secure: config.SMTP_PORT === 465,
auth: {
user: config.SMTP_USER,
pass: config.SMTP_PASSWORD,
},
});
export async function sendWithNodemailer({
to,
subject,
react,
}: {
to: string | string[];
subject: string;
react: React.ReactElement;
}) {
const html = ReactDOMServer.renderToStaticMarkup(react);
const info = await nodemailerTransport.sendMail({
from: config.RESEND_FROM, // 可配置为专用SMTP发件人
to: Array.isArray(to) ? to.join(',') : to,
subject,
html,
});
return info;
}
环境变量配置
在项目根目录的.env文件中添加:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-email@example.com
SMTP_PASSWORD=your-email-password
SendGrid集成指南
安装依赖
pnpm add @sendgrid/mail
创建SendGrid适配器
// packages/email/adapters/sendgrid.ts
import sendgrid from '@sendgrid/mail';
import { keys } from '../keys';
import ReactDOMServer from 'react-dom/server';
const config = keys();
sendgrid.setApiKey(config.SENDGRID_API_KEY!);
export async function sendWithSendGrid({
to,
subject,
react,
}: {
to: string | string[];
subject: string;
react: React.ReactElement;
}) {
const html = ReactDOMServer.renderToStaticMarkup(react);
const [result] = await sendgrid.send({
from: config.RESEND_FROM,
to: Array.isArray(to) ? to : [to],
subject,
html,
});
return result;
}
环境变量配置
SENDGRID_API_KEY=your-sendgrid-api-key
多服务商对比与选择
| 特性 | Resend | Nodemailer | SendGrid |
|---|---|---|---|
| 配置复杂度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 模板支持 | 内置React | 需手动渲染 | 部分支持 |
| 交付率 | 高 | 依赖SMTP服务器 | 高 |
| 价格模型 | 免费额度+按量付费 | 自托管/第三方SMTP | 免费额度+套餐 |
| 分析功能 | 基础 | 无 | 丰富 |
| 国内访问 | 需配置代理 | 取决于SMTP服务器 | 需配置代理 |
选择建议
- 开发/演示环境:优先使用Resend,配置简单且提供免费额度
- 中小流量生产环境:Nodemailer+第三方SMTP服务(如阿里云邮件推送)
- 高流量企业环境:SendGrid提供更完善的送达保障和数据分析
最佳实践与性能优化
1. 邮件发送队列
对于批量邮件发送,建议实现队列系统:
// 伪代码示例
import { Queue } from 'bullmq';
const emailQueue = new Queue('email-queue');
// 添加到队列
emailQueue.add('send-email', { to, subject, template: 'contact', props });
// worker处理
emailQueue.process(async (job) => {
const { to, subject, template, props } = job.data;
const Template = require(`../templates/${template}`).default;
return sendEmail({ to, subject, react: <Template {...props} /> });
});
2. 错误处理与重试策略
async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
try {
return await fn();
} catch (error) {
if (retries > 0) {
const delay = Math.pow(2, 3 - retries) * 1000; // 指数退避
await new Promise(resolve => setTimeout(resolve, delay));
return withRetry(fn, retries - 1);
}
throw error;
}
}
// 使用
await withRetry(() => sendEmail(options));
3. 模板复用与组件设计
// 创建可复用布局组件
// packages/email/components/Layout.tsx
import { Body, Container, Head, Html, Tailwind } from '@react-email/components';
import React from 'react';
type LayoutProps = {
children: React.ReactNode;
title: string;
preview?: string;
};
export const Layout = ({ children, title, preview }: LayoutProps) => (
<Tailwind>
<Html>
<Head>
<title>{title}</title>
</Head>
{preview && <Preview>{preview}</Preview>}
<Body className="bg-zinc-50 font-sans">
<Container className="mx-auto py-12">{children}</Container>
</Body>
</Html>
</Tailwind>
);
部署与监控
环境变量管理
在生产环境中,建议使用Vercel环境变量或类似服务管理敏感配置:
# Vercel环境变量配置示例
RESEND_FROM=notifications@yourdomain.com
RESEND_TOKEN=re_********************************
发送状态跟踪
集成应用监控工具跟踪邮件发送状态:
// 添加到邮件发送函数
import { captureException } from '@sentry/nextjs';
export async function sendEmail(/* ... */) {
try {
// 发送逻辑
// 记录成功指标
posthog.capture('email_sent', { to, subject });
return data;
} catch (error) {
// 错误跟踪
captureException(error);
posthog.capture('email_failed', { to, subject, error: error.message });
throw error;
}
}
总结与未来展望
next-forge邮件服务模块通过抽象化设计,实现了邮件发送逻辑与模板系统的分离,支持多种邮件服务商集成。目前默认使用Resend提供简洁的开发体验,同时可通过适配器模式扩展Nodemailer和SendGrid等服务。
未来版本计划添加:
- 邮件模板版本控制
- A/B测试功能
- 多语言模板支持
- 高级分析集成
通过本指南,开发者可根据项目需求选择合适的邮件解决方案,实现高效可靠的邮件通信功能。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



