Novu 官方 React Email 桥接应用模板深度解析:从npx novu init到首个邮件工作流上线
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
导读
packages/novu/src/commands/init/templates/app-react-email/ts/是 Novu CLI 初始化命令npx novu init生成的一套开箱即用的桥接(Bridge)应用模板:它以 Next.js 为运行载体,将 React Email 编写的邮件模板与 Novu 工作流无缝衔接。读完本文,你将掌握模板的整体目录结构、Bridge 端点的工作原理、工作流与 Zod Schema 的声明方式、React Email 模板的渲染链路,以及如何在本地一键触发你的第一条测试通知。
模板是什么:一条命令生成的 Code-First 通知应用
模板的说明文档(README-template.md)明确指出:这是一套由npx novu init引导生成的 Novu 桥接应用。它体现了 Novu 的"代码优先(Code-First)"理念——工作流、邮件模板、Schema 全部以 TypeScript 源码的形式存在于你的仓库中,而不是在云端拖拽配置。
从模板目录结构(packages/novu/src/commands/init/templates/app-react-email/ts/)可以看到,生成后的应用包含三大部分:
- Next.js 应用层:
app/下的页面与 API 路由(app/page.tsx、app/api/*),负责前端展示与 HTTP 入口; - Novu 工作流层:
app/novu/workflows/中以 TypeScript 定义的工作流及其入参 Schema; - 邮件模板层:
app/novu/emails/中基于 React Email 组件库编写的邮件组件。
该模板是模板家族的成员之一,同目录下还提供app-agent、app-agent-ai-sdk、app-agent-langchain等变体(见 templates 目录),而本模板的差异化定位是:用 React Email 编写可交互、可动态渲染的邮件内容,并以 email + in-app 双通道演示完整工作流。
快速启动:四种包管理器一键运行
模板 README 给出的启动方式极为简洁,四种包管理器任选其一:
npm run dev # or yarn dev # or pnpm dev # or bun dev启动后 Next.js 开发服务器默认监听http://localhost:4000,同时通过默认 Bridge 端点/api/novu与 Novu Cloud 同步状态。也就是说,你的工作流定义会被暴露为一个标准的 HTTP 端点,Novu Cloud 会拉取(同步)该端点上的工作流清单,并在运行时回调该端点执行对应步骤。
这里有两个关键概念值得展开:
- Bridge 端点:模板中即
app/api/novu/route.ts,它把工作流集合暴露为可被 Novu 服务调用的协议端点; - 端口约定:默认 4000 端口与
http://localhost:2022(Novu Dev Studio 本地开发工具的端口,在模板的app/api/dev-studio-status/route.ts与邮件按钮链接中均有引用)共同构成了本地开发环境。
Bridge 端点解剖:/api/novu如何把工作流暴露给 Novu
模板中最核心的一行代码位于 app/api/novu/route.ts:
import { serve } from '@novu/framework/next'; import { welcomeOnboardingEmail } from '../../novu/workflows'; // the workflows collection can hold as many workflow definitions as you need export const { GET, POST, OPTIONS } = serve({ workflows: [welcomeOnboardingEmail], });要点拆解:
serve来自@novu/framework/next:这是 Novu 框架为 Next.js App Router 提供的适配器,它实现了 Bridge 协议所需的全部 HTTP 方法——GET(供 Novu 发现/健康检查)、POST(执行工作流)、OPTIONS(CORS 预检),因此只需一行导出即可完成端点接入;workflows数组:注释明确说明"workflows collection 可以容纳任意多个工作流定义",新增工作流时只需在app/novu/workflows/index.ts中导出,并追加到该数组中;- 导入路径约定:模板中所有 API 路由统一通过
../../novu/workflows引用工作流,保持目录职责清晰。
作为对比,模板前端app/page.tsx中的连接检测逻辑(轮询/api/dev-studio-status)进一步印证了本地开发闭环:dev-studio-status路由会以 3 秒超时探测http://localhost:2022/.well-known/novu,只有 Dev Studio 返回合法的port与route字段时,页面才将状态置为connected。这意味着,工作流从"代码定义"到"云端可触发"的过程,在本地开发阶段由 Dev Studio 承担了同步与调试的职责。
第一个工作流:双通道的 welcome-onboarding-email
模板预置了一个可直接运行的工作流welcome-onboarding-email,完整定义位于 app/novu/workflows/welcome-onboarding-email/workflow.ts:
import { workflow } from '@novu/framework'; import { renderEmail } from '../../emails/novu-onboarding-email'; import { emailControlSchema, payloadSchema } from './schemas'; export const welcomeOnboardingEmail = workflow( 'welcome-onboarding-email', async ({ step, payload }) => { await step.email( 'send-email', async (controls) => { return { subject: controls.subject, body: renderEmail(controls, payload), }; }, { controlSchema: emailControlSchema, } ); await step.inApp('in-app-step', async () => { return { subject: payload.inAppSubject, body: payload.inAppBody, avatar: payload.inAppAvatar, }; }); }, { payloadSchema, } );理解workflow声明式 API
- 第一个参数是工作流 ID 字符串:
'welcome-onboarding-email',它在整个环境中唯一标识该工作流,也是后续触发(trigger)时使用的名称; - 第二个参数是执行函数:接收
{ step, payload },其中step暴露各类通道步骤(step.email、step.inApp等),payload是触发时携带的业务数据; - 第三个参数是配置对象:传入
payloadSchema,用于对触发载荷做类型校验与约束。
双通道演示:email + in-app
该工作流演示了两个最常用的步骤:
step.email('send-email', ...):邮件步骤,controls来自用户在 Novu 工作流编辑器/Studio 中可配置的控制项(对应emailControlSchema),步骤返回的{ subject, body }即为最终邮件内容。注意body通过renderEmail(controls, payload)由 React Email 组件实时渲染为 HTML 字符串;step.inApp('in-app-step', ...):应用内通知步骤,直接引用payload中的inAppSubject、inAppBody、inAppAvatar字段。这样一次触发即可同时投递"邮件 + 站内通知"两种渠道,是理解 Novu 多通道编排的最佳入门示例。
Schema 先行:Zod 驱动的类型安全
模板把入参校验抽象为独立的 schemas.ts,使用 Zod 定义两类 Schema:
payloadSchema(触发载荷):定义触发工作流时必须/可携带的业务数据,全部带默认值,因此空载荷也能触发成功:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
inAppSubject | string | **Welcome to Novu!** | 站内通知标题 |
inAppBody | string | This is an in-app notification powered by Novu. | 站内通知正文 |
inAppAvatar | string(url) | Novu GitHub 头像 URL | 站内通知头像 |
teamImage | string(url) | Spr.so 渐变图 URL | 邮件"用户邀请"区块团队头像 |
userImage | string(url) | react-email demo 用户图 URL | 邮件"用户邀请"区块用户头像 |
arrowImage | string(url) | react-email demo 箭头图 URL | 邮件"用户邀请"区块箭头图标 |
emailControlSchema(邮件控制项):定义邮件在 Novu 工作流画布中可被配置的"控制项",这与 Novu 可视化编辑器的控件(Controls)机制一一对应:
subject:string,默认A Successful Test on Novu!,控制邮件主题;showHeader:boolean,默认true,控制是否显示邮件顶部的 Novu Logo;components:数组,默认包含 heading / text / users / text / button 五个区块,每个元素包含type(枚举heading | text | button | code | users)、text、align(枚举left | center | right)。这套结构化的"区块数组"是模板演示的核心亮点——邮件内容本身成为可配置数据。
而 types.ts 通过z.infer从 Schema 反推 TypeScript 类型(PayloadSchema与ControlSchema),让邮件组件与工作流共享同一份类型契约,从编译期杜绝字段拼写错误。
React Email 渲染链路:从组件到 HTML
模板的邮件组件位于 app/novu/emails/novu-onboarding-email.tsx,其技术要点如下:
组件构成
- 从
@react-email/components引入Html、Head、Preview、Tailwind、Body、Container、Section、Heading、Text、Button、Row、Column、Img、CodeInline等全套 React Email 组件; - 通过
<Tailwind config={...}>自定义主题:新增brand: '#2250f4'、offwhite、blurwhite等颜色变量与 0/20/45px 间距变量,实现零额外 CSS 文件的邮件样式; - 组件 Props 类型为
NovuWelcomeEmailProps = ControlSchema & PayloadSchema,即把工作流的控制项与载荷合并注入。
区块渲染逻辑
邮件主体按components数组动态渲染五种区块:
- heading:渲染
<Heading as="h1">,对齐方式来自component.align; - button:渲染指向
http://localhost:2022(Novu Dev Studio)的黑色按钮; - text:渲染正文
<Text>; - users:渲染"用户邀请"三列头像行(
userImage、arrowImage、teamImage),模拟真实产品中的社交型通知; - code:使用
<CodeInline>展示内联代码片段。
导出renderEmail工具函数
文件底部是关键衔接点:
export function renderEmail(controls: ControlSchema, payload: PayloadSchema) { return render(<NovuWelcomeEmail {...controls} {...payload} />); }renderEmail调用@react-email/components的render将 React 组件树转为 HTML 字符串,供工作流中step.email的body使用。这正是"React Email 写模板、Novu 负责投递"的 Code-First 模式的精髓:邮件模板与业务代码同仓库、同类型、同版本管理。
触发工作流:模板自带的演示链路
模板不仅定义了工作流,还自带了一条完整的前后端触发演示链路:
1. 服务端触发 API:app/api/trigger/route.ts
export async function POST() { try { await welcomeOnboardingEmail.trigger({ to: process.env.NEXT_PUBLIC_NOVU_SUBSCRIBER_ID || '', payload: {}, }); return NextResponse.json({ message: 'Notification triggered successfully' }); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; console.error('Error triggering notification:', errorMessage); return NextResponse.json({ message: 'Error triggering notification', error: errorMessage }, { status: 500 }); } }- 通过
welcomeOnboardingEmail.trigger(...)以 SDK 方式直接触发工作流(而非走 REST API),to为订阅者 ID(取自环境变量NEXT_PUBLIC_NOVU_SUBSCRIBER_ID),payload为空对象——得益于 Schema 默认值,空载荷依然能产出完整通知; - 异常分支将错误信息以 500 状态码返回,便于在页面上排查。
2. 前端交互:app/page.tsx
页面加载时向/api/events上报一次访问埋点(透传到https://api.novu.co/v1/telemetry/measure,见 app/api/events/route.ts);同时每 3 秒轮询/api/dev-studio-status以展示"已连接"状态。点击页面按钮后调用/api/trigger触发通知,成功后弹出成功提示(3 秒后自动隐藏)。页面中还集成了NovuInbox组件(app/components/NotificationToast/Notifications.tsx),用于实时接收 in-app 通知。
3. 环境变量
从代码中可以确认模板运行所需的两个环境变量:
NEXT_PUBLIC_NOVU_SUBSCRIBER_ID:触发通知时使用的订阅者标识,前端可访问;NOVU_SECRET_KEY:服务端调用 Novu API(如遥测上报)时使用的密钥,以ApiKey前缀放在 Authorization 头中。
扩展指引:如何加入你自己的工作流
依据模板的代码组织方式,添加新工作流的推荐步骤为:
- 在
app/novu/workflows/下新建目录(如my-workflow/),参考welcome-onboarding-email拆分为workflow.ts、schemas.ts、types.ts三个文件; - 在
schemas.ts中用 Zod 定义payloadSchema与各步骤的controlSchema,保持字段默认值完备,确保空载荷也能演示; - 在
workflow.ts中用workflow()声明工作流,按需调用step.email、step.inApp、step.sms、step.push等步骤; - 在 app/novu/workflows/index.ts 中
export *新模块; - 把新工作流追加到 app/api/novu/route.ts 的
serve({ workflows: [...] })数组中。
至此,新工作流会随/api/novu端点被 Novu 发现,进入与welcome-onboarding-email完全相同的同步与触发链路。
小结
app-react-email模板是理解 Novu Code-First 通知基础设施的最佳切入点:它以 Next.js + React Email + Zod + Novu Framework 的组合,把"工作流声明、Schema 校验、邮件渲染、Bridge 同步、SDK 触发"整条链路浓缩在不到十个源文件中。无论你是想快速跑通第一条通知,还是准备把现有 Next.js 项目接入 Novu,都可以直接以该模板为蓝本开始改造。
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考