n8n:public-api について
n8n Public API v1 を拡張する開発者向けのスキルです。コントローラー、DTO、認可、ページネーション、OpenAPI、テストの規約を示し、公開契約を保つ変更を支援します。
主な機能
- 01コントローラーとサービスの境界
- 02厳格な公開 DTO 規約
- 03API キーと RBAC による認可
- 04カーソルベースの一覧ページネーション
- 05OpenAPI の生成と登録
- 06統合テストの指針
活用シーン
- 01Public API エンドポイントの追加
- 02レガシーエンドポイントの移行
- 03カーソルページネーション付き一覧の実装
- 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 キー認証と
PublicApiControllerRegistry
(packages/cli/src/public-api/public-api-controller.registry.ts)による公開エラー形式を使って /api/v1 にマウントされています。
ルールは 2 つの階層に分かれます。不変条件(決して破ってはいけません)とチームのデフォルト(既存の公開契約に反する場合を除き、従ってください)です。このスキルとコードの詳細が食い違う場合は、コードが優先されます。そのため、以下のファイルを開いてください。これはコードを確認する理由であり、チームのデフォルトを省略してよいという意味ではありません。
絶対に守るルール
- 新しいエンドポイントは、
v1/controllers/配下の@PublicApiControllerクラスとし、機能ごとに*.public.controller.tsを 1 つ作成します。 - 公開 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にします。追加するサービスメソッド、ハンドラー呼び出し、リポジトリメソッドのすべてに適用します。TypeORM の名称であるskip/takeは使用しないでください。skip/takeへの変換は、リポジトリ内の TypeORMfind呼び出しでのみ行います。公開クエリ文字列は引き続きcursor+limitです。offsetはデコードされたカーソルのフィールドで、サービスに渡される値であり、クライアント向けのパラメーターではありません。 - 更新には
PATCHではなく、オブジェクト全体を指定するPUTを使用します。成功したGETのレスポンスボディは、サーバー管理フィールド/不変フィールドを除き、同じリソースへのPUTのリクエストボディとして使用できるようにします(ラウンドトリップ)。 - 入力 DTO は厳密にし、出力 DTO は公開フィールドの許可リストにします。
- 実際のシークレット/トークンをレスポンスやエラー詳細に決して返さないでください。リソースのセンチネル/プレースホルダーでマスクするか、省略します。
PUTでそのセンチネルを返した場合は保持し、それ以外の値であれば置き換えます。詳細: 更新と書き込み専用のシークレット。 - 「接続/設定のテスト」エンドポイントでは、リクエスト本文を検証します(保存前にテスト)。
アーキテクチャ
公開ルートと内部ルートは、HTTP に依存しない 1 つの共有サービスを介する兄弟ルートです。互いを呼び出すことはありません。
GET /rest/tags → TagsController ┐ JWT auth, internal shape
├─→ TagService
GET /api/v1/tags → TagsPublicController ┘ API-key auth, public DTO
サービスの動作を再利用します。公開契約と内部契約が意図的に同一である場合に限り DTO を再利用します。それ以外の場合は、UI 向けの内部形式に依存しない、公開 API 専用の DTO を作成します。
編集する前に
以下を開いてください。こちらが正確な情報源であり、このスキルではありません。
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() して不要なフィールドを除去します。ルートごとに 1 つだけ指定してください。2 つ目の @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 |
認可(間違いやすい点)
@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 のリレーションは明示的に有効化する必要があるため、同じエンティティを扱う 2 つのルートでも 返す形状は異なることがあります。
- 入力 DTO は厳密にして、未知のフィールドや一部だけのフィールドが暗黙に受け入れられないようにしてください。
Z.class(shape, { strict: true })を使用します。 - シークレット:実際のシークレットは決して返さないでください。リソースのセンチネル値/プレースホルダーを使用するか、 省略してください。更新と書き込み専用シークレットを参照してください。
リストエンドポイント(カーソルページネーション)
tags.public.controller.ts のカーソル処理をコピーしてください。入力 DTO では
limit: publicApiPaginationSchema.limit と cursor: z.string().optional() を指定します。
スキーマから limit を取り出し、publicApiPaginationSchema 全体を展開しないでください
(このスキーマには offset も含まれていますが、Public API のクエリパラメーターとして使用してはいけません)。
共有ページネーションサービスの decodeCursor / encodeNextCursor を使用してください。カーソルは不透明な値です。
最後のページでは nextCursor: null とし、裸の配列ではなく { data, nextCursor } を返してください。
無効なカーソルは 400 になります。既存のエンドポイントのカーソルの意味はそのまま維持してください。
ただし、offset パラメーターは維持すべき仕様ではなく、取り除くべき不具合です。詳細:
リストエンドポイントとカーソルページネーション。
接続チェックリスト
v1/controllers/<feature>.public.controller.tsと、v1/controllers/index.tsでの副作用インポート。@n8n/api-typesに公開 DTO を追加し、バレル(src/dto/)からエクスポートします。- 権限レジストリに
@ApiKeyScopeの値が存在することを確認します。 - コントローラールートの 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を実行しないと、新規または変更したコントローラーが反映されないままになります。 - ルートの
@ApiTagsで新しいタグを使用する場合は、そのタグをv1/openapi.ymlのtags:配列に宣言してください。x-displayNameと、親タグのいずれかを指定するx-parentを設定します。子タグはアルファベット順に並べてください。GitBook は表示名からページ URL を生成するため、ドキュメントの URL を確認せずに既存のx-displayNameを変更しないでください。タグが宣言されていない場合、または順序が正しくない場合、openapi-tags.test.tsによって失敗します。 packages/nodes-base/nodes/N8n/n8n-api-coverage.jsonにルートを追加します。- テスト。
テスト
常に、正常系、入力検証エラー、API キーのスコープ不足、RBAC による拒否を網羅してください。ビジネス処理のカバレッジには、packages/cli/test/integration/public-api/ の利用を優先してください(実際の HTTP と DB を使用します)。モックサービスを使った単体テストでは代替できません。該当するケース(カーソルページ、未検出/競合、機密フィールドが含まれないこと、認証情報の保持/置換、マイグレーション契約)を追加してください。テストマトリックスを参照し、最も近い既存のテストに合わせてください。