com um clique
add-api-endpoint
// Misskey の REST API エンドポイント (/api/<category>/<name>) を NestJS DI + meta/paramDef 規約で追加する。バックエンドに新しい API ルートを足す時に必ず使う。endpoint-list.ts への手動登録、e2e テスト、misskey-js 再生成、CHANGELOG までの一連の手順を含む。
// Misskey の REST API エンドポイント (/api/<category>/<name>) を NestJS DI + meta/paramDef 規約で追加する。バックエンドに新しい API ルートを足す時に必ず使う。endpoint-list.ts への手動登録、e2e テスト、misskey-js 再生成、CHANGELOG までの一連の手順を含む。
Misskey の i18n キーを追加・修正する。locales/ja-JP.yml のみ編集可能で、他言語ファイル (en-US.yml 等 39 言語) は Crowdin の自動配信先のため絶対に触らない。型は packages/i18n が ja-JP.yml から自動再生成する。frontend からは i18n.ts.<key> または i18n.tsx.<key>(...) で参照する。
Misskey フロントエンドの新規 Vue 3 コンポーネントを追加する。Mk* 命名 / SPDX (HTML コメント) / <script setup lang="ts"> / <style lang="scss" module> / *.stories.impl.ts 併設の規約をまとめて適用する。新しい共有 UI コンポーネントを packages/frontend/src/components/ に作る時に使う。
Claude Code セッションのコンテキスト窓消費を agents/skills/MCP/rules/CLAUDE.md ごとに見える化し、肥大化と冗長コンポーネントを検出して節約候補を提示する。"コンテキスト消費を見せて"、"context budget"、"context audit"、"トークン内訳"、"これ以上 MCP 入る?" 等の発話で起動する。
Misskey の TypeORM マイグレーションを公式 CLI (migration:generate / migration:create) で正しく生成し、SPDX ヘッダー付与・up/down 整合・check-migrations 確認まで誘導する。エンティティのスキーマ変更を含むあらゆる DB 変更、または手書き SQL によるデータ移行が必要な時に使用する。
| name | add-api-endpoint |
| description | Misskey の REST API エンドポイント (/api/<category>/<name>) を NestJS DI + meta/paramDef 規約で追加する。バックエンドに新しい API ルートを足す時に必ず使う。endpoint-list.ts への手動登録、e2e テスト、misskey-js 再生成、CHANGELOG までの一連の手順を含む。 |
packages/backend/src/server/api/endpoints/<category>/<name>.ts に新規エンドポイントを追加するためのワークフロー。手順 4 (endpoint-list.ts 登録) を忘れると 404 になる 点に最大の注意を払う。
meta / paramDef を変えたら misskey-js の再生成が必須。pnpm build-misskey-js-with-types を忘れると CI の check-misskey-js-autogen で必ず落ちる。meta.errors の各 id は UUID。重複させない (既存全 UUID と衝突確認)。packages/backend/src/server/api/endpoints/<category>/<name>.ts に新規作成する。<category> は機能領域 (例: notes, users, admin/announcements)。
冒頭に SPDX ヘッダーを必ず付ける:
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
endpoints/ping.ts をベースに書く。認証不要・パラメータなし・小さなレスポンスの例:
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
export const meta = {
tags: ['<tag>'],
requireCredential: false,
res: {
type: 'object',
optional: false, nullable: false,
properties: {
// ...
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
) {
super(meta, paramDef, async (ps, me) => {
// 実装
});
}
}
endpoints/notes/create.ts を参照する。要点:
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ApiError } from '@/server/api/error.js';
import { DI } from '@/di-symbols.js';
// import ms from 'ms'; // limit.duration に ms('1hour') 等を渡すとき (default import)
export const meta = {
tags: ['notes'],
requireCredential: true, // 認証必須なら true
prohibitMoved: false, // moved user を拒否するか
kind: 'write:notes', // OAuth scope (requireCredential 時に必須)
limit: {
duration: 3600000, // ms('1hour')
max: 300,
},
errors: {
noSuchNote: {
message: 'No such note.',
code: 'NO_SUCH_NOTE',
id: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', // ★ UUID v4 を必ず生成 (`x`=hex, `y`=8/9/a/b)。下の「UUID 生成」を参照
},
},
res: {
type: 'object',
optional: false, nullable: false,
ref: 'Note', // packed entity に揃える場合
},
} as const;
export const paramDef = {
type: 'object',
properties: {
noteId: { type: 'string', format: 'misskey:id' },
},
required: ['noteId'],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
) {
super(meta, paramDef, async (ps, me) => {
const note = await this.notesRepository.findOneBy({ id: ps.noteId });
if (note == null) throw new ApiError(meta.errors.noSuchNote);
// 実装
});
}
}
| フィールド | 用途 |
|---|---|
tags | OpenAPI タグ (機能領域) |
requireCredential | 認証必須か |
requireModerator / requireAdmin | 権限制限 |
prohibitMoved | アカウント移行済ユーザーを拒否 |
kind | OAuth scope (read:notes / write:notes 等)。requireCredential: true 時必須 |
limit | レート制限 ({ duration, max, key?, minInterval? }) |
errors | エラー定義。各要素に message / code / id (UUID v4) 必須 |
res | JSON Schema or ref: '<EntityName>' (packed entity 参照) |
requireFile | ファイルアップロード必須 |
secure | secure cookie 必要 |
allowGet | GET メソッド許可 |
cacheSec | レスポンスキャッシュ秒数 |
description | OpenAPI 説明 |
詳細は endpoints.ts の型定義 (lines 11-125) を参照。
JSON Schema (AJV) ベースだが、Misskey 拡張を使える:
format: 'misskey:id' — ID 文字列バリデーションallOf / anyOf / oneOf — 複合条件default — デフォルト値詳細は endpoint-base.ts を参照。
「公開 API エラーとして API クライアントに返したいもの」は必ず throw new ApiError(meta.errors.<key>) を使う。meta.errors に列挙した上で ApiError でラップしないと、misskey-js 側の型情報に出ず、レスポンスも 500 になる。第 2 引数で追加情報を渡せる:
throw new ApiError(meta.errors.invalidParam, { reason: 'too short' });
一方で、想定外の例外 (DB 不整合 / 下層サービスの bug など) を握り潰すために try/catch で ApiError に変換するのは避ける。既存 endpoint も「期待される業務エラーは ApiError に変換し、それ以外は throw err; で再 throw する」という二段構えになっている。packages/backend/src/server/api/endpoints/notes/create.ts の catch 節 (末尾の throw err;) を参照。生の throw を全面禁止すると未知例外も 200 で潰れて debug が困難になるので、このバランスを保つ。
詳細は error.ts の ApiError クラスを参照。
node -e "console.log(crypto.randomUUID())"
その UUID が他のエンドポイントの id と衝突していないか必ず確認:
grep -r "id: '<生成した UUID>'" packages/backend/src/server/api/endpoints/
packages/backend/src/server/api/endpoint-list.ts の同カテゴリ末尾に 1 行追加する(既存の並びを崩さない):
export * as '<category>/<name>' from './endpoints/<category>/<name>.js';
ファイル冒頭のコメント (When you add new endpoint, you should add it to this file.) の通り、このリストが API ルーティングの単一の真実。忘れると 404。
EndpointsModule.ts がこのファイルの全エクスポートを Object.entries() で反復し、NestJS provider (provide: 'ep:<path>') を生成する。
packages/backend/test/e2e/endpoints.ts に対応する describe / test を追加する。api() ヘルパーで叩く:
describe('<category>/<name>', () => {
test('正常系', async () => {
const res = await api('<category>/<name>', { /* params */ }, alice);
assert.strictEqual(res.status, 200);
});
});
実行: pnpm --filter backend test:e2e
meta / paramDef / res を変えたら必ず実行する:
pnpm build-misskey-js-with-types
これで以下が更新される:
packages/backend/built/api.json (OpenAPI spec)packages/misskey-js/generator/api.jsonpackages/misskey-js/src/autogen/*.ts (TypeScript 型)PR に packages/misskey-js/src/autogen/ 配下の差分が含まれていないと、CI の check-misskey-js-autogen で落ちる。
pnpm --filter backend lint
(typecheck = tsgo --noEmit / ESLint = eslint)
ユーザー影響がある (新機能 / 既存挙動変更) なら、CHANGELOG.md の ## Unreleased → ### Server に 1 行追加する (AGENTS.md §CHANGELOG 参照):
- Feat: /api/<category>/<name> を追加
純粋なリファクタや内部用なら不要。