mcpField
N
n8n:public-api

n8n:public-api

github.com/n8n-io开发209.8k91 次安装1 天前更新
代码仓库

关于 n8n:public-api

面向 n8n Public API v1 开发者的技能,说明控制器、DTO、授权、分页、OpenAPI 和测试规范,帮助在扩展接口时维护公开契约。

主要功能

  1. 01控制器与服务边界
  2. 02严格的公开 DTO 规范
  3. 03API 密钥与 RBAC 授权
  4. 04基于游标的列表分页
  5. 05OpenAPI 生成与接入
  6. 06集成测试指导

适用场景

  1. 01新增 Public API 端点
  2. 02迁移旧版端点
  3. 03实现游标分页列表
  4. 04测试授权与输入验证

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/n8n-io/n8n --skill n8n:public-api

安装后在 Claude Code 中输入/n8n:public-api确认已加载。

附带文件reference.md

详细介绍

公共 API v1

公共 API v1 位于 packages/cli/src/public-api/v1/,挂载在 /api/v1, 使用 API 密钥进行身份验证,并通过 PublicApiControllerRegistry (packages/cli/src/public-api/public-api-controller.registry.ts)统一格式化公共错误。

规则分为两个层级:不可违反的规则(绝不能破坏)和团队默认规范(除非现有公共契约另有要求,否则应遵循)。如果本规范与代码在某个细节上不一致,以代码为准——请打开下面的文件。因此应检查代码,而不是借此忽略团队默认规范。

不可妥协的规则

  • 新端点必须是在 v1/controllers/ 下的 @PublicApiController 类,每个功能对应一个 *.public.controller.ts 文件。
  • 公共 API 和内部 REST 是彼此独立的 HTTP 接口。公共控制器绝不调用内部控制器或端点;两者复用同一个服务。
  • 控制器和处理器必须委托给服务——绝不导入存储库或使用 Container.get(…Repository)(no-repository-in-public-api-handler)。
  • 输入和输出必须使用 @n8n/api-types 中的 DTO;每个 JSON 路由都必须声明 @ApiResponse(Dto)。
  • 通过 v1/controllers/index.ts 中的副作用导入注册每个控制器(否则 public-api-controllers.test.ts 会失败)。
  • 迁移旧端点时不得更改其公共契约。

这些是 n8n-local-rules ESLint 规则(参见 packages/cli/eslint.config.mjs),不得通过内联方式禁用(no-public-api-guardrail-disable)。其中的 off 允许列表仅涵盖已有的旧文件——只能缩减,不要添加条目。

团队默认规范

  • 编写能够自我说明的代码。模式、装饰器和测试本身就应清楚地表达规则,无需注释。
  • 列表端点:使用基于游标的分页(内部 API 同时使用基于游标和基于页码的分页——不要照搬内部端点的模型)。
  • 分页参数始终为 offset 和 limit——应用于你新增的服务方法、处理器调用和存储库方法。绝不使用 skip/take(TypeORM 的命名)。仅在存储库的 TypeORM find 调用中将其转换为 skip/take。公共查询字符串仍使用 cursor + limit;offset 是传递给服务的已解码游标字段,绝不是面向客户端的参数。
  • 更新:使用完整对象 PUT,而非 PATCH。成功的 GET 响应体应可作为同一资源的 PUT 请求体(往返),服务器管理字段或不可变字段除外。
  • 输入 DTO 必须严格;输出 DTO 是公共字段的允许列表。
  • 响应或错误详情中绝不返回真实密钥或令牌——使用资源的哨兵值/占位符进行屏蔽(或省略)。在 PUT 中回传该哨兵值表示保留;任何其他值都会替换。详情参见: 更新和只写密钥。
  • “测试连接/配置”端点必须验证请求体(先测试,再保存)。

架构

公共和内部路由是同一个与 HTTP 无关的服务之上的同级路由;两者互不调用。

GET /rest/tags    → TagsController         ┐  JWT auth, internal shape
                                            ├─→ TagService
GET /api/v1/tags  → TagsPublicController   ┘  API-key auth, public DTO

复用服务行为。只有在公共契约和内部契约有意保持一致时才复用 DTO;否则应创建专用的公共 DTO,不要依赖面向 UI 的内部结构。

编辑前

请先查看以下内容——它们才是事实来源,而不是本技能:

  • v1/controllers/——参考 tags.public.controller.ts(列表 + 游标)或 workflows.public.controller.ts(@Param + @ProjectScope)复制结构,并参考 index.ts 中的导出汇总。
  • packages/@n8n/decorators/src/controller/ 中的装饰器: public-api-controller.ts、api-key-scope.ts、api-response.ts、 api-error-response.ts、api-summary.ts、api-description.ts、api-tags.ts、 route.ts、scoped.ts、args.ts、licensed.ts。
  • OpenAPI 生成器(读取上述装饰器,无需为控制器路由手写 YAML): v1/openapi-gen/generate.ts、 v1/openapi-gen/decorator-routes.ts。
  • 分页辅助工具:v1/shared/services/pagination.service.ts (decodeCursor、encodeNextCursor)。
  • DTO:packages/@n8n/api-types/src/dto/。
  • 门控测试:v1/__tests__/public-api-controllers.test.ts、 v1/__tests__/scope-parity.test.ts、 v1/openapi-gen/__tests__/generated-spec-drift.test.ts。
  • 此资源的内部控制器及其相邻的功能测试。

声明控制器

控制器是一个标记了 @PublicApiController('/base') 的类,它通过构造函数注入 共享服务,并将请求委托给该服务。请从 v1/controllers/ 中操作类型和身份验证 模型相同的现有控制器复制结构;仅复用适用的部分。所有装饰器均来自 @n8n/decorators:

装饰器用途
@PublicApiController('/base')类标记;在 /api/v1/base 挂载路由。
@Get/@Post/@Put/@Patch/@Delete('/path')路由方法。
@ApiKeyScope('res:action')检查 API 密钥授权。
@ProjectScope/@GlobalScope('res:action')检查用户 RBAC 权限。
@ApiResponse(status) / @ApiResponse(status, Dto)成功状态码 +(可选)输出 DTO;注册器会对返回值调用 .parse() 并剥离多余字段。每条路由只能有一个——第二个 @ApiResponse 会抛出异常。204 不能携带 DTO 或二进制主体,否则会抛出异常。对于二进制响应主体(以 { body, headers? } 形式返回),请使用 @ApiResponse(status, { mediaType, description?, headers? })——参见二进制响应主体。
@ApiErrorResponse(status)声明额外记录的非 2xx 状态码(例如 404、409)。需要多个时可叠加使用。400/401/403 会自动添加(分别对应存在请求体或查询参数、始终添加、存在 @ApiKeyScope)——无需自行声明。
@ApiSummary(text) / @ApiDescription(text) / @ApiTags([...])OpenAPI 摘要/描述/标签。无论传入顺序如何,@ApiTags 都会按字母顺序排序。它们都是可选的,但每条实际路由都应添加。
@Query / @Body / @Param('name')通过 Z.class DTO/路径参数绑定并验证。默认情况下,@Body 使用 JSON;@Body({ mediaType: 'multipart/form-data', uploadLimits }) 则接收 multipart/form-data 请求体——参见请求体媒体类型。
@Licensed('feat')根据单个 BooleanLicenseFeature 对路由进行门控;PublicApiControllerRegistry 会运行自己的许可证中间件(在身份验证/@ApiKeyScope/@ProjectScope 或 @GlobalScope 之后、处理程序之前),并对未经许可的请求返回 403。只接受一个功能——如果门控条件是任一/全部组合(例如 LicenseState.isProvisioningLicensed(),即 feat:saml 或 feat:oidc),@Licensed 无法表达;应像当前内部的 provisioning.controller.ee.ts/role-mapping-rule.controller.ee.ts 一样,在处理程序中手动检查(失败时抛出 ForbiddenError)。

授权(容易出错)

  • @ApiKeyScope(API 密钥获授的权限)与 @ProjectScope/@GlobalScope (用户可执行的操作)彼此独立。当模型两者都需要时,请同时使用。
  • 将每个路径参数命名为 {resource}Id(例如 workflowId、credentialId、 projectId 等)——绝不要使用通用的 :id / {id}。这是 Public API 的 命名约定:它能让 API 自我说明,并为类型化 SDK 代码生成提供真实的参数名, 而不是 id。@ProjectScope 也会原样读取 req.params,不会将 id 重新映射—— 它按确切的键名解析授权(workflowId、credentialId、projectId、dataTableId、 等),因此在 @ProjectScope 路由上使用通用的 id 往往会直接失败;@GlobalScope 或未限定作用域的路由不会以相同方式失败,但仍应遵循此约定。
  • @ApiKeyScope 接受字符串、{ anyOf: [...] } 或 { allOf: [...] }——绝不能 使用裸数组。该作用域必须存在于权限注册表(@n8n/permissions 中的 API_KEY_RESOURCES)里;scope-parity.test.ts 会在作用域无对应项时失败。

DTO

  • 明确构建公共响应结构;不要返回 ORM 实体并依赖 @ApiResponse 剥离字段来隐藏内容。
  • 将输出 DTO 视为允许列表。重新检查嵌套关系、所有权字段、令牌和加密值。
  • 输出 DTO 限制的是返回哪些字段,而不是这些字段可以包含哪些值。注册表会根据 DTO 解析处理程序的返回值,因此不符合架构的值会导致 500。架构应足够宽松,以容纳 现有行中可能包含的任何内容。
  • 根据路由已加载的关系构建响应,而不是根据实体类型。TypeORM 关系需要显式选择, 因此,同一实体上的两个路由可以返回不同的结构。
  • 输入 DTO 应设置为严格模式,避免静默接受未知字段或不完整字段: Z.class(shape, { strict: true })。
  • 密钥:绝不返回真实密钥;应使用资源的哨兵值/占位符(或省略)。参见更新和只写密钥。

列表端点(游标分页)

请复制 tags.public.controller.ts 中的游标流程。输入 DTO 接受 limit: publicApiPaginationSchema.limit 和 cursor: z.string().optional() — 从架构中单独取出 limit,绝不要展开整个 publicApiPaginationSchema (它还导出了 offset,而 offset 绝不能作为 Public API 查询参数)。使用共享分页服务中的 decodeCursor / encodeNextCursor;游标是不透明的;返回 { data, nextCursor }(绝不要返回裸数组), 最后一页的 nextCursor: null;无效游标会返回 400。按原样保留现有端点的游标语义——但 offset 参数属于应移除的缺陷,不是应保留的契约。详情请参阅: 列表端点和游标分页。

接线检查清单

  1. v1/controllers/<feature>.public.controller.ts + 在 v1/controllers/index.ts 中添加副作用导入。
  2. 在 @n8n/api-types 中添加公共 DTO + 从 barrel(src/dto/)导出。
  3. 权限注册表中存在 @ApiKeyScope 值。
  4. 不要手动编写 OpenAPI 路径或控制器路由的 x-required-scope——生成器(v1/openapi-gen/generate.ts)会根据你的装饰器(@ApiSummary/@ApiDescription/@ApiTags/@ApiKeyScope/ @ApiResponse/@ApiErrorResponse)生成它们。运行完整的 pnpm build,并提交重新生成的 handlers/<feature>/spec/paths/*.generated.yml 片段和 openapi.decorator-routes.generated.yml—— 如果它们过期,generated-spec-drift.test.ts 会导致 CI 失败。修改控制器后,仅运行 pnpm run build:data 并不足够:它会基于已编译的 dist/ 运行生成器,因此除非先运行了 tsc,否则新建或修改的控制器不会显示出来。
  5. 如果路由在 @ApiTags 中使用了新标签,请在 v1/openapi.yml 的 tags: 数组中声明该标签。为其设置 x-displayName 和一个指向父标签的 x-parent。子标签按字母顺序排列。 GitBook 会根据显示名称生成页面 URL,因此修改现有的 x-displayName 前,请确认文档 URL 不受影响。 如果未声明标签或标签顺序错误,openapi-tags.test.ts 会导致测试失败。
  6. 将路由添加到 packages/nodes-base/nodes/N8n/n8n-api-coverage.json。
  7. 测试。

测试

始终覆盖:正常路径、输入验证失败、缺少 API 密钥权限范围、RBAC 拒绝。 优先在 packages/cli/test/integration/public-api/ 中覆盖业务路径(真实 HTTP + DB);模拟服务的单元 测试不能替代它。添加适用的用例(游标分页、 未找到/冲突、不包含敏感字段、凭证保留/替换、迁移契约)——参见测试矩阵。参照最接近的现有测试。

更多详情(reference.md)

评论

支持 Markdown