Novu 开源 API 服务(@novu/api-service)全指南:本地运行、Endpoint 扩展、OpenAPI 规范与数据迁移

发布时间:2026/9/10 23:29:41

Novu 开源 API 服务(@novu/api-service)全指南:本地运行、Endpoint 扩展、OpenAPI 规范与数据迁移 Novu 开源 API 服务novu/api-service全指南本地运行、Endpoint 扩展、OpenAPI 规范与数据迁移【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novuNovu 是一款面向智能体Agent与产品的开源通信基础设施。其核心网关由apps/api目录下的novu/api-service承担——这是一个基于NestJS构建的 RESTful API 服务为 Dashboard、外部客户端与官方 SDK 提供统一访问入口。本文以该服务的 README 为骨架结合仓库源码深入讲解如何本地启动与测试 API、如何遵循规范新增一个 Endpoint、如何维护 OpenAPI 规范含 SDK 生成装饰器以及如何在涉及底层数据硬依赖时编写与运行数据库迁移脚本。读完本文你可以无障碍地参与或二次开发 Novu 的 API 层。一、novu/api-service在仓库中的定位与目录结构从仓库根目录的模块布局可以清楚看到Novu 采用 Nx pnpm workspace 管理多个应用与库。API 服务位于 apps/api 目录其 npm 包名为novu/api-service见 apps/api/package.json在nx.json中被标记为type:app应用。API 层并非孤立存在它与仓库内多个库协同工作这也解释了为什么 Endpoint 扩展往往只需薄薄一层 Controller Usecase目录 / 包职责apps/api/src/app按业务域组织的 Controller / Module / Usecase / Commandsubscribers、topics、events、integrations、workflows 等 40 业务模块apps/api/src/app/auth/framework认证相关装饰器与守卫RequireAuthentication、ExternalApiAccessibleapps/api/src/app/shared/framework/swaggerOpenAPI 文档构建、SDK 元数据装饰器、文档注入与排序组件apps/api/migrationsMongo 数据迁移脚本按“变更描述/变更动作”组织另有clickhouse-migrations/存放 ClickHouse SQL 迁移libs/dal数据访问层EnvironmentRepository、IntegrationRepository等 Mongo Repositorylibs/internal-sdk由 OpenAPI 文档通过 Speakeasy 生成的官方 SDKlibs/application-generic跨应用共享框架例如ExternalApiAccessible、OAuthAccessible装饰器、Swagger 安全方案常量入口方面Nest 应用由 apps/api/src/main.ts 引导根模块见 apps/api/src/app.module.ts。novu/api-service的当前版本为3.19.1核心技术栈为 NestJS 11、Express 5、TypeScript 5.6并通过nestjs/swagger7.4 输出 OpenAPI 文档。二、本地启动 API 服务2.1 前置环境准备API 依赖 MongoDB、Redis 等基础设施通过 docker/local 与 docker/community 下的 docker-compose 文件组织。完整的环境搭建步骤请参照仓库内文档 docs/community/run-in-local-machine.mdx其中包含依赖安装pnpm workspace 安装、环境变量文件生成仓库根scripts/setup-env-files.js与本地依赖服务启动等步骤。2.2 启动命令在完成本地环境准备后按照 API README 的说明在仓库根目录执行# 以监听watch模式启动 API 服务 $ npm run start:api该命令映射到根 package.json 中的start:api: cross-env nx run novu/api-service:start即通过 Nx 执行novu/api-service项目的 start 目标。如果你进入 apps/api 目录工作apps/api/package.json 还提供了更细粒度的脚本# 等价的 Nest watch 模式启动 $ npm run start:dev # nest start --watch # 调试模式含 --inspect $ npm run start:debug # 测试环境NODE_ENVtest $ npm run start:test # 直接运行编译产物生产模式 $ npm run start:prod # node dist/main.js服务默认监听3000 端口启动后即可访问各业务 Endpoint 以及 OpenAPI 文档界面。三、测试策略单元测试与 E2E 测试3.1 单元测试在 apps/api/package.json 中单元测试通过 Mocha 执行收集src/**/*.spec.ts文件并预设NODE_ENVtest、NOVU_ENTERPRISEtrue等环境变量EE 特性开关依赖 enterprise/packages 的本地 link# 单元测试依赖 Nx 缓存的 metadata 构建pretest 自动执行 pnpm build:metadata $ npm run test测试文件与实现同目录共存例如迁移脚本的单元测试就放在 apps/api/migrations/integration-scheme-update/add-integration-identifier-migration.spec.ts各业务模块下也维护了unit/子目录存放用例测试。3.2 E2E 测试E2E 测试位于 apps/api/e2e以setup.ts做全局初始化测试用例散落在src/**/*.e2e.ts与*.e2e-ee.ts中。从脚本可以看出 E2E 被拆成了两套执行入口分别对应 Novu v0 与 v2 两代 API 语义# v0 语义 E2Egrep #novu-v0 标签 $ npm run test:e2e:novu-v0 # v2 语义 E2E按分片调度脚本运行 $ npm run test:e2e:novu-v2 # v2 语义、仅社区版CE部分 $ npm run test:e2e:novu-v2-ce注意 E2E 中会大量覆盖装饰器与认证行为。例如 apps/api/src/app/agents/e2e/sendblue-connect-auth.e2e.ts 的注释就明确说明测试要验证 Endpoint 不仅能被 Dashboard JWT 访问还应当能被ExternalApiAccessible()标记的用户 API-Key 访问。这提示我们在新增 Endpoint 时认证注解的取舍直接影响 E2E 的通过与否。四、如何新增一个 Endpoint核心规范这是 README 中最具工程价值的部分Novu 通过一套“Controller Usecase 装饰器约定”保证了 API 层的可维护性、OpenAPI 文档质量与 SDK 生成质量。我们结合 subscribersV1.controller.ts 的真实实现逐条讲解。4.1 第一步选择正确的 Controller若新 Endpoint 属于已有实体如 Subscriber、Topic、Integration请把方法直接添加到该实体的现有 Controller 中若它是全新的业务实体则新建一个 Controller并在对应的 Modulexxx.module.ts的controllers数组中注册。可以参考 subscribersV1.module.ts 的组织方式。业务逻辑采用Usecase 模式Controller 只负责 HTTP 层的参数解析与鉴权声明真正执行逻辑的 Usecase 通过 Command 对象接收数据。例如 get-subscribers.command.ts 继承EnvironmentCommand并用class-validator的IsNumber()/IsOptional()校验page、limit参数对应 Usecase 位于 get-subscribers.usecase.ts。4.2 第二步为方法添加正确的装饰器HTTP 方法与参数装饰器来自nestjs/commonGet/Post/Put/Delete——定义 HTTP 方法Param/Query/Body——定义参数来源。认证与访问控制装饰器Novu 自定义是 API 层最重要的两条约定RequireAuthentication()——为 Endpoint 挂上认证守卫并让 Novu Web AppDashboard可访问。其实现位于 auth.decorator.ts社区版环境下它等价于applyDecorators(UseGuards(CommunityUserAuthGuard), ApiBearerAuth(...))即要求 JWT 登录态当isEEAuthEnabled()开启企业版时则委派给novu/ee-auth的对应实现。ExternalApiAccessible()——声明该 Endpoint 可被外部 API携带 Api-Key 的用户以及官方 Novu SDK访问。该装饰器定义于novu/application-generic并在 external-api.decorator.ts 中统一 re-export。// 真实示例来自 subscribersV1.controller.ts截取含 5 组典型装饰器 Get(/:subscriberId) ExternalApiAccessible() RequireAuthentication() ApiExcludeEndpoint() ApiResponse(SubscriberResponseDto) ApiOperation({ summary: Retrieve a subscriber, description: Retrieve a subscriber by its unique key identifier **subscriberId**., deprecated: true, }) async getSubscriber( UserSession() user: UserSessionData, Param(subscriberId) subscriberId: string ): PromiseSubscriberResponseDto { /* … */ }需要说明上面例子同时出现了ExternalApiAccessible()与RequireAuthentication()这是 Novu 的常见组合——前者让 Swagger 安全声明与 SDK 侧认可该接口后者保证用户态会话同样可用ApiExcludeEndpoint()、ApiOperation()等则来自nestjs/swagger用于文档展示控制。4.3 命名约定Controller 方法命名遵循“动词 实体名”的统一格式语义方法名格式要求单条查询getEntityName如getSubscriber创建createEntityName如createSubscriber更新updateEntityName如updateSubscriber删除deleteEntityName如removeSubscriber列表 / 全量list前缀如listSubscribers必须实现分页并用SdkUsePagination标注分页参数子资源 / 非常规操作沿用上述命名 SDK 分组装饰器见下节在 subscribersV1.controller.ts 中listSubscribers方法正是列表风格的范本Get()SdkUsePagination()Query() query: GetSubscribersDto内部调用GetSubscribersCommand.create({ organizationId, environmentId, page, limit })后交给 Usecase返回PaginatedResponseDtoSubscriberResponseDto。4.4 SDK 相关装饰器让 OpenAPI 直接驱动官方 SDKNovu 的官方 SDKlibs/internal-sdk由 OpenAPI 文档经 Speakeasy 生成因此这些装饰器本质上是在 OpenAPI 规范里写入x-speakeasy-*扩展字段源码见 sdk.decorators.ts装饰器生成的 OpenAPI 扩展用途SdkUsePagination(override?)x-speakeasy-pagination声明offsetLimit类型分页输入为pagetype: page与limit可用 override 自定义参数名参数输出结果取自$.data.resultArray。SDK 会据此暴露支持异步迭代的分页能力显著改善 DX见 sdk.decorators.ts#L103-L123SdkGroupName(name)x-speakeasy-group为 SDK 做端点分组使用.分隔符即可表达子资源层级例如Subscribers.Notifications对应getSubscriberNotifications。原始资源则定义为 OpenAPI Tag见 sdk.decorators.ts#L22-L24SdkMethodName(name)x-speakeasy-name-override对非常规操作覆盖 SDK 生成的方法名例如getSubscriberNotifications见 sdk.decorators.ts#L12-L14SdkIgnorePath(...)x-speakeasy-ignore跳过某路径不生成到 SDKSdkUsageExample(...)x-speakeasy-usage-example为 SDK 提供使用示例DocumentationIgnore()x-ignore在 OpenAPI 文档中忽略该操作因此新增 Endpoint 时的最低限度是写清楚ExternalApiAccessible() 方法命名若要进入官方 SDK还需要按“实体/子资源”语义组合SdkGroupName、SdkMethodName对分页列表补充SdkUsePagination。SDK 的重新生成由 apps/api/package.json 的generate:sdk脚本触发speakeasy run后执行构建。五、OpenAPISwagger与 Spectral 校验5.1 文档的生成与访问Novu API 使用nestjs/swagger实时生成 OpenAPI 规范。文档装配逻辑集中在 swagger.controller.ts用DocumentBuilder配置标题Novu API、版本、两种安全方案API-KeyAuthorization: ApiKey novu_secret_keyswagger.controller.ts#L15-L21与Bearer JWTswagger.controller.ts#L22-L26通过SwaggerModule.setup(openapi, ...)挂载文档并暴露openapi.json与openapi.yamlswagger.controller.ts#L131-L137旧版 Swagger UI 挂在/api路径并标记DEPRECATEDswagger.controller.ts#L121-L129。因此本地开发时可以访问http://localhost:3000/openapiSwagger UI 浏览界面http://localhost:3000/openapi.yaml//openapi.json机器可读的规范文档。此外仓库还内置了两套配套产物与工具脚本# 导出 OpenAPI JSON 到 swagger-spec.json供文档站点等消费 $ npm run generate:swagger # ts-node exportOpenAPIJSON.ts5.2 用 Spectral 校验规范风格为保持 OpenAPI 文档的一致性与质量Novu 引入Spectral做规范校验stoplight/spectral-cli在 devDependencies 中。PR 中会通过 GitHub Action 自动执行本地可在API 已启动的前提下运行$ npm run lint:openapi该命令实际执行spectral lint http://127.0.0.1:${PORT:-3000}/openapi.yaml见 apps/api/package.json即对本地运行中的 API 拉取 YAML 进行 lint。若输出 warnings / errorsGitHub Action 将无法通过必须修复后才能合并。而这些修复不是直接改文档文件而是通过调整nestjs/swagger系列装饰器如ApiProperty、ApiOperation、ApiTags以及上述x-speakeasy-*扩展来改变文档内容。六、数据迁移Migrations规范6.1 什么场景需要迁移迁移服务于对数据库实体上的特定数据存在硬依赖的功能——例如某个新特性要求所有Integration记录必须携带identifier字段。这类迁移由Novu Cloud 与 Novu Self-Hosted 用户共同执行以支撑新版本发布。6.2 如何运行迁移npm run migration脚本定义在 apps/api/package.jsonmigration: cross-env NODE_ENVlocal MIGRATIONtrue ts-node --transpileOnly脚本本身不指定迁移文件待运行的迁移路径以位置参数传入脚本被文档与 release notes 引用因此脚本命名必须保持稳定。以 README 中的示例——运行 Add Integration Identifier 迁移为例npm run migration -- ./migrations/integration-scheme-update/add-integration-identifier-migration.ts对应迁移文件真实存在于 apps/api/migrations/integration-scheme-update/add-integration-identifier-migration.ts。执行时NODE_ENVlocal会在本地环境生效该目录下的 ClickHouse 迁移则使用独立的clickhouse-migrationsCLI见clickhouse:migrate:local/clickhouse:migrate:prod脚本迁移定义在 apps/api/migrations/clickhouse-migrations以 SQL 文件组织。6.3 目录与文件命名约定迁移统一存放在./migrations目录遵循两级命名结构./migrations/CHANGE_DESCRIPTION/CHANGE_ACTION.tsCHANGE_DESCRIPTION变更描述每个描述目录下可以有1 个或多个CHANGE_ACTION.ts动作脚本CHANGE_ACTION具体的迁移动作。README 中给出的范例与仓库现状完全一致migrations/ └── integration-scheme-update/ ├── add-integration-identifier-migration.ts └── add-primary-priority-migration.ts实际目录中还额外存在update-primary-for-disabled-novu-integrations.ts与对应的*.spec.ts测试且整仓已有encrypt-api-keys/、encrypt-credentials/、subscribers/、seen-read-support/、preference-centralization/等大量“一次变更 多个动作脚本”的实例见 apps/api/migrations印证了该约定的通用性。6.4 一个真实迁移的内部实现来看 Add Integration Identifier 迁移的核心逻辑add-integration-identifier-migration.ts#L36-L60它直接通过novu/dal的IntegrationRepository与EnvironmentRepository拉取全部Integration记录为每条记录计算出 payload 后按_id/_environmentId/_organizationId复合条件执行$set更新export async function addIntegrationIdentifierMigration() { const integrationRepository new IntegrationRepository(); const environmentRepository new EnvironmentRepository(); const integrations await integrationRepository.find({}); for (const integration of integrations) { const updatePayload await getUpdatePayload(integration, environmentRepository); await integrationRepository.update( { _id: integration._id, _environmentId: integration._environmentId, _organizationId: integration._organizationId }, { $set: updatePayload } ); } }同一文件中还提供了addIntegrationIdentifierMigrationBatched()分批版本——利用findBatch以500 条为一批迭代适合海量数据场景。这也是仓库里编写迁移的常见模式提供“一次性全量版”与“分批版”并配套.spec.ts单元测试来验证 payload 计算逻辑。值得注意的是这类迁移脚本默认没有强制可重入幂等设计运行时需要自行保证变更语义正确。七、开发自查清单结合 README 规范与源码约束为novu/api-service贡献一个新 Endpoint 时建议按如下清单自查路由归属优先加入既有实体的 Controller仅在确属新实体时新建 Controller 并注册到对应 Module方法命名get/create/update/delete EntityName列表接口使用list前缀认证双端需要 Dashboard 访问时加RequireAuthentication()需要 API-Key/SDK 访问时加ExternalApiAccessible()分页完备列表接口必须支持page/limit分页并返回PaginatedResponseDto同时标注SdkUsePagination()SDK 语义用SdkGroupName.表示子资源层级与SdkMethodName让生成 SDK 的方法名可读文档与 lint本地启动后执行npm run lint:openapi确保 Spectral 无新增 warning/error需要重新导出规范时执行npm run generate:swagger数据变更若依赖实体上的既有数据按./migrations/描述/动作.ts组织迁移并覆盖单元测试回归验证运行npm run test单元测试与对应语义的test:e2e:novu-v0/v2E2E 用例。通过以上约束Novu 得以在“业务域模块众多、Controller 频繁新增”的前提下维持 REST 语义的一致性、OpenAPI 文档的机器可校验性以及官方 SDK 与后端契约的自动同步——这正是 README 中“装饰器 命名 目录约定”这套工程规范的核心价值。【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/10 23:29:41

高校奖学金管理系统设计与实现:规则引擎与区块链技术应用

1. 计算机系奖学金管理系统概述计算机系奖学金管理系统是针对高校计算机专业设计的专项管理软件,旨在实现奖学金评审全流程数字化。这个系统通常包含学生信息管理、成绩计算、评审规则配置、申请审核、公示公告等核心模块,能够显著提升院系奖学金管理效率…

2026/9/10 23:29:40

Vue3组合式API+Pinia实战:从零搭建待办清单应用

最近带几个学前端的朋友做小项目,我发现大多数人卡住的地方往往不是单个语法点,而是不知道怎么把组合式 API、Pinia 状态管理、单文件组件这些零散的东西串到一个完整项目里。所以这次我挑了待办清单这个经典到不能再经典的项目,用 Vue3 组合…

2026/9/10 23:29:40

Python自定义迭代器设计与高效应用指南

1. 自定义迭代器设计概述在Python编程中,迭代器(Iterator)是一个非常重要的概念,它允许我们按顺序访问集合中的元素而不需要暴露其底层实现。自定义迭代器的设计能力,是区分初级和高级Python开发者的关键技能之一。我曾…

2026/9/11 0:19:46

Pathfinder人群仿真模型创建与优化指南

1. Pathfinder人群仿真模型创建基础Pathfinder作为专业的人群动态仿真软件,其模型创建流程遵循典型的"场景搭建-行为定义-仿真验证"工作流。新建项目时建议优先确定坐标系和单位制,建筑行业通常采用米制单位,而某些工业场景可能需要…

2026/9/11 0:19:46

LSTM与Adaboost融合的区间预测方法及Matlab实现

1. 项目概述:集成学习与区间预测的创新融合这个项目本质上是在解决一个预测科学中的经典难题:如何在高噪声、非线性的多变量时间序列数据中,实现更准确的预测区间估计。我们融合了三种关键技术——LSTM神经网络、Adaboost集成学习和ABKDE&…

2026/9/11 0:19:46

PyTorch原生CNN实战:MNIST手写数字识别完整闭环

简介:本资源是一份面向机器学习初学者与课程设计学生的Python实践项目,聚焦卷积神经网络(CNN)在MNIST手写数字识别任务中的完整实现。项目基于PyTorch框架,涵盖模型构建、训练、测试及结果可视化全流程,适合…

2026/9/11 0:19:46

YOLOv5-v7.0 OpenCV C++ 部署全链路指南

简介:本资源是一套面向C开发者与计算机视觉工程师的YOLOv5-v7.0多任务部署实践包,聚焦图像分类、目标检测与实例分割三大核心能力在OpenCV环境下的高效落地。针对工业部署中常见的跨平台、低依赖、高实时性需求,提供开箱即用的C推理demo&…

2026/9/11 0:19:46

PostgreSQL性能优化:sys_stat_statements模块详解

1. sys_stat_statements 模块概述sys_stat_statements 是 PostgreSQL 数据库中的一个扩展模块,它能够跟踪服务器执行的所有 SQL 语句的统计信息。这个模块对于数据库性能调优和 SQL 优化来说是不可或缺的工具。通过它,DBA 和开发人员可以清晰地了解哪些 …

2026/9/11 0:14:45

延安门头招牌设计技术指南与行业痛点解析

1. 延安门头招牌设计的行业现状与核心痛点延安作为革命老区,近年来城市形象升级需求显著。门头招牌作为商业门面的"第一张名片",其设计质量直接影响店铺引流效果。根据我们团队在陕北地区三年的实地调研,延安商户在招牌设计上普遍面…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码