Claude Media
Claude CodeでStrapiのコンテンツタイプを定義する手順と落とし穴

Claude CodeでStrapiのコンテンツタイプを定義する手順と落とし穴

Strapi 5のschema.jsonをClaude Codeに編集させる手順を、再起動とビルドの扱い、フィールド名変更でデータが消える落とし穴、CLAUDE.mdの規約例までを解説します。

Claude CodeでStrapiのコンテンツタイプを扱う全体像

Strapiのコンテンツタイプは、管理画面のContent-type Builderで作る方法と、プロジェクト内のschema.jsonを直接書く方法の2通りで定義できます。Claude Codeに任せるのは後者です。管理画面の操作はClaude Codeからは行えませんが、schema.jsonならファイルとして編集でき、差分もGitで追えます。

ただし、schema.jsonの編集は見た目より危険です。フィールド名を1つ変えただけで、既存データが空になる場合があります。Claude Codeに書かせる前に、守らせたいルールを決めておくのが先です。

先に押さえる3点

  • Content-type Builderは開発環境(Development)専用で、本番ではスキーマを変えられない
  • schema.jsonのキーを直接書き換えると、データが引き継がれないおそれがある。改名はstrapi rename:field(5.57.0以降)かContent-type Builderの確認を通す
  • 変更後の型定義はstrapi ts:generate-typesで作り直す

作業は5段で進む

各段の詳細は後の節で扱います。ここでは、段ごとに何を見れば次へ進めるかだけ並べます。

手順

Claude Codeでコンテンツタイプを足す順序

  1. 1

    雛形を作る

    src/api/<名前>/content-types/<名前>/schema.jsonができていることを確かめます。

  2. 2

    属性を書かせる

    Gitの差分で、変わったのがattributesの追加行だけかを見ます。

  3. 3

    起動して反映を確かめる

    strapi developのログにエラーがなく、再起動が流れたかを見ます。

  4. 4

    型を作り直す

    型定義ファイルに新しい属性が入り、フロントエンドのビルドが通るかを見ます。

  5. 5

    改名はコマンドで

    ./database/migrationsに、新しいマイグレーションファイルが1つ増えたかを見ます。

schema.jsonはどこにあり、何が書かれているか

コンテンツタイプのモデルは./src/api/[api-name]/content-types/[content-type-name]/に置かれ、中核がschema.jsonです。コンポーネントは./src/componentsの下に、カテゴリ名のサブフォルダごとに置きます。Content-type Builderで作った場合も、strapi generateで作った場合も、schema.jsonは自動で生成されます。

schema.jsonは、次の4つの塊でできています。

  • 設定: kind(collectionTypeかsingleType)と、保存先のcollectionName
  • 情報: infoのdisplayName・singularName・pluralNameなど
  • 属性: attributesにフィールドの型と制約を並べる
  • オプション: optionsのdraftAndPublishなど

singularNameとpluralNameはAPIのルートやテーブル名の元になり、どちらもkebab-caseで書きます。strapi generateでは、単数形と複数形を同じ名前にできません。

公式のモデル解説に沿った形で、レストランの定義を書くと次のようになります。

{
  "kind": "collectionType",
  "collectionName": "restaurants",
  "info": {
    "displayName": "Restaurant",
    "singularName": "restaurant",
    "pluralName": "restaurants",
    "description": ""
  },
  "options": { "draftAndPublish": true },
  "attributes": {
    "title": { "type": "string", "minLength": 3, "maxLength": 99, "unique": true },
    "description": { "type": "text", "required": true },
    "slug": { "type": "uid", "targetField": "title" }
  }
}

属性のtypeには、string・text・richtext・uid・integer・datetime・boolean・jsonのほか、media・relation・component・dynamiczoneなどがあります。required・unique・minLengthなどの検証キーは、属性の中に並べます。private: trueにすると、その属性はサーバーの応答から外れます。

雛形はstrapi generateに作らせ、属性をClaude Codeに書かせる

ファイルを手で一から作ると、フォルダ構成やkindを取り違えがちです。雛形はstrapi generateのcontent-typeに作らせ、そこへ属性を足す分担が安全です。

npm run strapi generate
# 「content-type」を選び、kebab-caseの単数形・複数形を指定する

generateは、コントローラーやサービス、データベースのマイグレーションファイルも作れます。コンテンツタイプだけを作るなら、content-typeを選ぶ流れです。インタラクティブCLIで作ったコンテンツタイプは、draftAndPublishの既定がfalseになります。モデル側の既定はtrueなので、下書きと公開を使うなら、生成後にoptionsを見直します。

雛形ができたら、Claude Codeには次のように頼みます。

src/api/restaurant/content-types/restaurant/schema.json に
属性を足してください。
- address: 必須の短い文字列
- openedAt: 日付時刻
- menus: Menu と多対多
変更前に、既存フィールドの名前を変えていないか差分で報告してください。

最後の一文が効きます。フィールド名の変更はデータ消失に直結するため、変更の有無を毎回報告させます。

CLAUDE.mdに書いておく規約の例

毎回の依頼文に書き足すより、CLAUDE.mdに固定したほうが抜けません。Strapi向けの断片は、次のようになります。

## Strapi のスキーマ規約
- コンテンツタイプは src/api/*/content-types/*/schema.json を編集する。dist/ は触らない
- 既存フィールド名・singularName・pluralName・collectionName は変更しない
- フィールド名を変えたいときは編集せず、`npm run strapi rename:field <uid> <旧> <新>` を提案する
- 属性を足したら `npm run strapi ts:generate-types` を実行する
- スキーマ変更後は `npm run develop` で起動し、エラーが出ないことを確認する

「dist/は触らない」は、Strapiのビルド成果物が次のビルドで置き換わるためです。公式も、フォルダ整理用のgroups.jsonについて「ソース側を編集し、distの方は編集しない」と述べています。

再起動とビルド — いつ反映されるか

schema.jsonを編集したあとの反映は、起動方法で変わります。

strapi developはファイルウォッチャーを持ち、必要なときにアプリケーションを再起動します。開発中はこちらで回すのが前提です。strapi startにはウォッチャーがないため、本番相当の起動でschema.jsonを書き換えても、再起動するまで反映されない構成だと考えておくのが安全です。ビルド済みの成果物を読む設定ファイルでは、公式が「再ビルドと再起動で反映される」と明記しています。

strapi developの説明には「必要なとき」としか書かれておらず、schema.jsonの変更がどの条件で再起動を起こすかまでは載っていません。Claude Codeにスキーマを書かせた直後は、ログに再起動が流れたかを見て、流れていなければ手で起動し直してください。

Content-type Builderが開発環境専用である点も、運用に効きます。本番でスキーマを編集する想定はなく、変更はコードとして開発環境で作り、デプロイで本番に届けます。schema.jsonをClaude Codeに書かせる流れは、この前提と相性が良い構成です。

フォルダ整理のgroups.jsonもClaude Codeに任せられる

Content-type Builderの左のナビに出るフォルダは、src/content-structure/groups.jsonに保存されます。ファイルはコードと一緒にバージョン管理され、デプロイで環境間を移ります。

  • フォルダの階層は3段まで。1つのコンテンツタイプが属せるフォルダは1つ
  • 同じ親の下で、フォルダ名は大文字小文字を区別せずに重複できない
  • 手書きでおかしな参照があると、起動時に修復または無視され、[content-structure]で始まるログが出る
  • データ転送(Data Transfer)はこのファイルを運ばない

起動時は寛容に修復されますが、Content-type Builderからの保存は厳格で、規則に反する構造は拒否されます。コンポーネントはこのファイルに含まれず、フォルダ分けもできません。

フィールド名の変更でデータが消える — マイグレーションの落とし穴

最大の落とし穴はここです。公式のContent-type Builderの説明には、次の挙動が明記されています。

落とし穴

schema.jsonのキー名を直接変えると、既存データが失われうる

5.57.0以降のContent-type Builderでは、フィールドを改名して保存するときに確認ダイアログが出ます。確認すると、列を作り直さずカラム名を変えるので、既存データが残ります。5.57.0より前は、この引き継ぎがありませんでした。型・リレーション・コンポーネントを同時に変えたときは、保存されず空のフィールドが作られます。

Claude Codeがschema.jsonのキーを直接書き換えると、この確認を経ずに旧来の扱いになり得ます。ここで使うのがstrapi rename:fieldです。

npm run strapi rename:field api::article.article title heading

このコマンドは、./database/migrationsにマイグレーションファイルを作り、そのパスを表示します。中身は、カラム(またはテーブル)を作り直さずに名前を変える処理で、次回のアプリケーション起動時に1回だけ実行されます。コマンドラインは確認を取れないため、ポリモーフィックリレーションのように移行できない改名は、スキーマを変えずに拒否されます。

そのほかの落とし穴を、表にまとめます。

操作起きること
フィールド名を変える(5.57.0より前)起きること既存データが引き継がれない
名前変更と同時に型・リレーション・コンポーネントを変える起きることデータは保存されず、空で作り直される
コンテンツタイプやコンポーネントのAPI IDを変える起きること新しいテーブルが作られ、旧テーブルのデータは管理画面から見えなくなる
コンポーネントを削除する起きること使われている全コンテンツタイプ・エントリから消える
コンテンツタイプを削除する起きること管理画面のエントリは消えるが、データベースのデータは残る

3行目のAPI ID変更は、rename:fieldの対象外です。singularNameやcollectionNameをClaude Codeに整えさせないよう、先ほどのCLAUDE.mdで禁じているのはこのためです。

リレーションとi18nをschema.jsonで書くときの注意

リレーションは、type: "relation"にrelationとtargetを指定して書きます。relationにはoneToOne・oneToMany・manyToOne・manyToManyなどがあります。双方向にするときは、所有する側がinversedBy、逆の側がmappedByを持ちます。たとえば植物と持ち主なら、植物側にmanyToOneとinversedBy: "plants"、持ち主側にoneToManyとmappedBy: "owner"を書きます。

Claude Codeにリレーションを書かせたら、片方の定義だけで終わっていないかを見ます。相手側のファイルにもmappedByまたはinversedByが入ったかを、差分で確かめてください。targetはAPI形式のUID(api::plant.plantのような形)で指定します。

多言語化は、属性ごとにpluginOptionsのi18nでlocalized: trueを指定します。コンテンツタイプ側のoptionsで下書き公開を切り替える点と合わせ、Content-type Builderのチェックボックスに対応するキーが、schema.jsonでは別々の場所にあります。

型定義の再生成を自動化する

TypeScriptのプロジェクトでは、属性を足すたびにstrapi ts:generate-typesでスキーマの型を作り直します。Claude Codeのhooksに載せれば、schema.jsonの編集後に自動で走ります。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | grep -q 'schema.json$' && npm run strapi ts:generate-types || true"
          }
        ]
      }
    ]
  }
}

hooksの書き方そのものは、Packerのhook設定の記事にも別の例があります。ts:generate-typesの出力先を--out-dirで変えるのは、型を別のディレクトリに作って既存の型と比べたいときくらいで、通常は使いません。公式も、型が機能するにはデフォルトのtypesディレクトリが必要だと注意しています。

Strapi AIとClaude Codeはどう違うか

Strapi自身にも、自然言語でコンテンツタイプを作るStrapi AIがあります。管理画面の右下から使うアシスタントで、Growthプランで利用でき、月1,000クレジットが含まれます。既存のStrapiやフロントエンドのコードを取り込んだり、Figmaや画像から構造を取り出したりもできます。

使い分けの軸は、変更をコードの差分として残したいかどうかです。Strapi AIは管理画面の操作で、Content-type Builderと同じく開発環境で保存します。Claude Codeはschema.jsonを直接編集するので、レビュー・テスト・マイグレーションファイルの追加までを、通常の開発フローに乗せられます。マイグレーションの安全な回し方は、Drizzle ORMのマイグレーション運用と同じ考え方で、生成物を人間が読んでから適用する形になります。

まとめ

Strapiのschema.jsonは、Claude Codeに向いた定義ファイルです。ただ、追加は任せやすい一方、改名・削除はデータを巻き込みます。追加は任せ、改名はrename:fieldに切り出し、削除は人間が決める。この線引きをCLAUDE.mdに書いておくのが、いちばん事故を減らします。

この記事を共有:XはてブLinkedIn