next-forge邮件服务:Nodemailer与SendGrid集成指南

next-forge邮件服务:Nodemailer与SendGrid集成指南

【免费下载链接】next-forge A production-grade boilerplate for modern Next.js apps. 【免费下载链接】next-forge 项目地址: https://gitcode.com/GitHub_Trending/ne/next-forge

邮件服务架构概览

next-forge作为生产级Next.js应用模板,其邮件服务模块采用分层架构设计,核心基于React Email组件系统构建,支持多邮件服务商无缝切换。以下是邮件服务的核心组件关系:

mermaid

邮件服务模块位于packages/email目录,提供统一的邮件发送接口,同时在apps/email目录下提供独立的邮件开发环境,支持模板预览和调试。

核心依赖与配置体系

依赖组件分析

next-forge邮件服务的核心依赖如下表所示:

依赖包版本作用
@react-email/components0.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

多服务商对比与选择

特性ResendNodemailerSendGrid
配置复杂度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
模板支持内置React需手动渲染部分支持
交付率依赖SMTP服务器
价格模型免费额度+按量付费自托管/第三方SMTP免费额度+套餐
分析功能基础丰富
国内访问需配置代理取决于SMTP服务器需配置代理

选择建议

mermaid

  • 开发/演示环境:优先使用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测试功能
  • 多语言模板支持
  • 高级分析集成

通过本指南,开发者可根据项目需求选择合适的邮件解决方案,实现高效可靠的邮件通信功能。

【免费下载链接】next-forge A production-grade boilerplate for modern Next.js apps. 【免费下载链接】next-forge 项目地址: https://gitcode.com/GitHub_Trending/ne/next-forge

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值