Strapi v4は2026年4月30日でEnd Of Lifeを迎え、2026年6月9日公開の4.26.2が最終版になりました。この記事では、4.26.2で作ったプロジェクトを npx @strapi/upgrade major で5.58.0へ移行した実行結果をもとに、v5で変わった点と、移行で手作業が残る箇所を整理します。Strapiそのものの仕組み、最新版の動作要件、料金表はStrapiとは?読み方・使い方と料金を最新版5.55で解説【2026年9月】にまとめています。
まとめ:v5移行の自動変換範囲と手作業の対象
- v5の中心は「Document」という単位です。Draft & Publishが有効な場合、公開済みコンテンツはロケールごとに下書き行と公開行を持ち、共通の
documentIdで束ねられます。未公開のコンテンツは下書き行だけです。 - RESTのレスポンスは
data.attributesの入れ子がなくなり、リレーションのdataの入れ子も消えました。数値のidで1件を取るURLは404になり、documentIdで取り直す必要があります。 npx @strapi/upgrade majorは依存の更新と13本のcodemodを実行しますが、publishedAtで公開する書き方やフロントエンドのpublicationStateは変換されず、エラーも出ません。- codemodの対象外にも、REST入力の検証(未知の項目は400)、予約語になった
status属性、作成と同時のファイルアップロード廃止など、手で直す変更があります。 - ライフサイクルフックは、公開すると
afterCreate、公開を取り消すとafterDeleteが発火します。v4の感覚で書いた通知処理は二重に動くため、Document Service middlewareへ移します。 - フロントエンドは
Strapi-Response-Format: v4ヘッダー(REST)とv4CompatibilityMode(GraphQL)で旧形式を返させながら段階的に直せます。
Strapi v5の変更点:Documentを単位にしたデータ管理
下書きと公開版を別の行で持つDraft & Publish
v4では1件のエントリーが1行で、publishedAt に日時が入っていれば公開、null なら下書きでした。公開中の記事を書き換えると、その変更は保存した瞬間に公開側へ出ます。
v5では、1つのDocumentが下書き版と公開版を別の行として持ちます。公開中の記事を編集しても変わるのは下書き行だけで、公開行は「公開」操作をするまで元のままです。v4で作った公開済みの記事1件と下書き1件を移行したところ、DBは次の3行になりました。
| id | document_id | title | published_at |
|---|---|---|---|
| 1 | teioh2kt… | Published in v4 | 日時あり(公開行) |
| 2 | kyscjyz9… | Draft in v4 | null(下書き行) |
| 3 | teioh2kt… | Published in v4 | null(移行で追加された下書き行) |
行数は公開済みの件数だけ増えます。多言語(i18n)を使っている場合は、ロケールごとにさらに行が分かれます。DBを直接集計しているバッチやBIツールは、published_at が入った行だけを数えるよう条件を足さないと、公開済み記事を二重に数えます。
REST APIのレスポンス形式とdocumentId
同じ記事をv4とv5の GET /api/articles で取得した結果です(日時の列は省略)。
// v4.26.2
{"data":[{"id":1,"attributes":{"title":"Published in v4","body":"hello"}}],
"meta":{"pagination":{"page":1,"pageSize":25,"pageCount":1,"total":1}}}
// v5.58.0(移行後)
{"data":[{"id":1,"title":"Published in v4","body":"hello",
"documentId":"teioh2kt2pgwvdpsq4q3rtdt"}],
"meta":{"pagination":{"page":1,"pageSize":25,"pageCount":1,"total":1}}}
フロントエンドで item.attributes.title と書いていた箇所は、すべて item.title に変わります。v5のレスポンスにも id は残っていますが、GET /api/articles/1 は404でした。1件取得・更新・削除のURLには24文字の documentId を使います。記事の詳細ページのURLに数値IDを使っているサイトは、旧IDから documentId を引く対応表などを用意すれば既存URLを維持できます。URL自体を変更する場合は、旧URLからのリダイレクトも設計します。
Entity Service APIからDocument Service APIへの置き換え
サーバー側のコードでデータを読み書きする strapi.entityService は非推奨になり、strapi.documents(uid) で使うDocument Service APIに置き換わりました。メソッドは findOne()・findFirst()・findMany()・create()・update()・delete()・count() と、Draft & Publishが有効な型だけで使える publish()・unpublish()・discardDraft() です。v4の findPage() に相当するメソッドはなく、ページングは findMany() の引数で指定します。
// v4
await strapi.entityService.findOne('api::article.article', 1);
// v5
await strapi.documents('api::article.article').findOne({
documentId: 'phmiu35dj5ci4z29tkhug0uz',
status: 'published',
});
status を省くと下書き版が返ります。公開中の内容を返すAPIを自作している場合は、status: 'published' の指定漏れがないか確認します。
v5で利用できる編集機能と必要なライセンス
v5ではコンテンツの編集機能が増えましたが、多くは有料のCMSライセンスが必要です。セルフホストでも同じ区分で、無料のCommunityで使えるものは限られます。
| 機能 | 内容 | 必要なCMSプラン |
|---|---|---|
| i18n(多言語) | v5でコアに統合 | Community(無料) |
| Preview | フロントエンドの表示を管理画面で確認 | Community(無料) |
| Live Preview | 編集内容をリアルタイムに反映 | Growth・Enterprise |
| Content History | 過去の版を表示・復元 | Growth・Enterprise |
| Releases | 複数の記事をまとめて公開・予約 | Growth・Enterprise |
| Review Workflows | 公開前の承認フロー | Enterprise |
| Strapi AI | 型設計・翻訳・画像alt生成(プラン別) | Growth:5.30以上/Enterprise:5.57以上・BYOK・翻訳と画像のみ |
Growthは公式CMS料金表で月45ドル(3席込み)です。Strapi AIはGrowthで月1,000クレジットが付きます。Enterpriseでは5.57以降、BYOKプラグインと自社のAIプロバイダーのキーで翻訳・画像メタデータ生成を利用できますが、Content-Type BuilderのAIアシスタントは対象外です。Strapi Cloudのホスティング料金を含む料金表の全体はStrapiの総合解説記事で扱っています。
開発基盤の変更:Viteへの移行とhelper-pluginの廃止
管理画面の既定バンドラーはViteになりました。また、Strapiが用意するWebpackの依存パッケージ向けエイリアスは主要なものだけに絞られ、従来の暗黙の依存解決を前提とするプラグインは修正が必要です。管理画面を拡張するプラグインで多用されていた @strapi/helper-plugin は削除され、機能ごとに @strapi/strapi/admin や @strapi/design-system へ移っています。Design Systemもv2に上がり、こちらはcodemodがありません。
プラグインの雛形作成とビルドは @strapi/sdk-plugin が担います。Marketplaceで使っているプラグインがv5対応版を出しているかは、移行に着手する前に確認します。
v4からv5への移行手順:4.26.2から5.58.0へのアップグレード実行結果
ここからは、Intel Mac・Node.js v22.23.3上で [email protected] のTypeScriptプロジェクト(SQLite)を作り、記事の型・公開ロールの権限・ライフサイクルフック・entityService を使った初期データ投入を入れた状態から、2026年10月19日に移行した記録です。
移行前の準備:4.26.2への更新・バックアップ・Node.jsの版
upgradeツールは、いまのメジャー版の最新(v4なら4.26.2)に揃っていることを前提にしています。先に次の順で準備します。
npx @strapi/upgrade minor # v4の最新(4.26.2)へ
# SQLiteはStrapiを正常終了し、DBへの接続を閉じてからコピーする
mkdir -p backup
cp .tmp/data.db backup/data.db
git switch -c upgrade-v5
v4の古いマイナー版から4.26.2へ上げる段階で、管理画面のビルドが Cannot find module 'react' で落ちることがあります。公式の移行FAQによると、新しいv4は react・react-dom・react-router-dom・styled-components を package.json に明示することを前提にしているためで、新規に作った4.26.2のプロジェクトから版をコピーして入れ直せば直ります。
Node.jsの版にも順序があります。4.26.2の engines は >=18.0.0 <=22.x.x で、5.58.0は >=20.0.0 <=26.x.x です。v4のまま起動して確認する工程があるため、移行作業はNode.js 22で行い、v5へ上がったあとにNode.js 24へ移すのが安全です。
upgradeツールの–dryによる書き換え範囲の事前確認
upgradeツールには、ファイルを書き換えずにcodemodの結果だけを見る --dry(-n)があります。upgradeツールの公式リファレンスのとおり、このモードでは package.json も依存も変わりません。
npx @strapi/upgrade major --dry # 影響するファイル数だけを表示
npx @strapi/upgrade codemods ls # codemodの一覧
npx @strapi/upgrade to 5.57.0 # 版を指定して上げる
4.26.2のプロジェクトで major --dry を実行すると、node_modules を入れていない状態でも約5秒で終わり、13本のcodemodがそれぞれ何ファイルに当たるかを表で表示しました。実行後の git status に変更はありません。ログの1行目は「Upgrading from v4.26.2 to v5.58.0」で、major は5.0.0ではなく、その時点のv5の最新まで一度に上げます。.npmrc の min-release-age などで公開直後の版を入れない運用をしている場合は、to で版を指定します。ただし to は「現メジャーの最新に揃っているか」の確認を飛ばすため、4.26.2への更新を先に済ませてから使います。
npx @strapi/upgrade majorで適用されたcodemod
npx @strapi/upgrade major --yes
ツールは要件の確認、codemodの適用、依存の更新、npm install の4段階で進みます。今回のプロジェクトで実際にファイルを書き換えたのは次の6本でした(2026年9月30日に5.55.1で行った移行でも同じ6本です)。
| codemod | 変更されたファイル | 変更内容 |
|---|---|---|
| comment out lifecycle files | lifecycles.ts | 全体をコメントアウト |
| dependency remove strapi plugin i18n | package.json | @strapi/plugin-i18nを削除 |
| dependency upgrade react router dom | package.json | 5.3.4 → ^6.0.0 |
| dependency upgrade styled components | package.json | 5.3.3 → ^6.0.0 |
| entity service document service | src/index.ts | entityServiceをdocuments()へ |
| dependency better sqlite3 | package.json | 11.10.0 → 12.8.0 |
残りの7本(helper-pluginの置き換え、S3の認証情報の移動、設定の名前空間のuid化など)は、対象コードがないか、依存関係がすでに変換先の条件を満たしていたため、変更なしでした。codemodの処理はここまで約4秒です。
最後の npm install は、v4のときの node_modules が残っていたため @strapi/[email protected] のpeer依存が解決できず、ERESOLVE で止まりました。5.55.1でも同じ止まり方でした。package.json の書き換えは済んでいるので、node_modules と package-lock.json を消して入れ直せば通ります。今回はインストールに約4分5秒、管理画面のビルドに約1分22秒かかりました。
rm -rf node_modules package-lock.json
npm install
npm run build
公式手順では、codemodが変換しきれなかった箇所にコード中の __TODO__ という目印が入るとされています。今回の小さなプロジェクトでは、src 配下に目印は1つも入りませんでした。次の節のとおり、目印がなくても動作が変わった箇所は残っていました。
初回起動で自動実行されるデータ移行
v5で最初に npm run start すると、DBの移行スクリプトが順に走ります。ログに出た移行は 5.0.0-rename-identifiers-longer-than-max-length、5.0.0-02-created-document-id、5.0.0-03-created-locale、5.0.0-04-created-published-at、5.0.0-05-drop-slug-fields-index、5.0.0-06-add-document-id-indexes、公開済みの記事に下書き行を追加する core::5.0.0-discard-drafts、管理画面ユーザーの言語コード dk を da に直す admin::migrate-prefered-language-dk-to-da、リッチテキストとBlocksに保存されたメディアURLから署名を外す upload::unsign-richtext-and-blocks-urls の計9本です。移行前の記事2件(移行後は下書き・公開版を合わせて3行)の環境では合計約0.5秒でした。件数が多い本番DBでは時間がかかるため、公開サイトを止める時間帯に実行します。
移行は一方向です。v5で起動したDBをv4に戻す仕組みはなく、戻すときは移行前のバックアップを復元し、コードもv4のブランチへ戻します。
codemodが変換しきれない箇所:publishedAtとpublicationState
codemodは strapi.entityService.create() を strapi.documents().create() に書き換えましたが、引数はv4のまま残しました。
await strapi.documents('api::article.article').create(
{ data: { title: 'Published in v4', body: 'hello', publishedAt: new Date() } }
);
v4ではこれで公開済みの記事ができました。5.58.0で同じコードを実行すると、作られたのは下書き行1つだけで、公開行はできませんでした。エラーや警告も出ません。v5で作成と同時に公開するには、status: 'published' を渡すか、作成後に publish() を呼びます。
await strapi.documents('api::article.article').create({
data: { title: 'direct publish' },
status: 'published',
});
フロントエンド側も同じです。v4で下書きを含めて取得する ?publicationState=preview をv5.58.0へ送ると、400エラーにはならず、パラメータが無視されて公開版1件だけが返りました。v5で下書きを取るパラメータは ?status=draft です。プレビュー画面が「なぜか下書きが出ない」状態になったら、このパラメータを疑います。
なお ?status=draft は、Publicロールに find を許可していると認証なしで通ります。今回の移行後の環境でも、未公開の「Draft in v4」と、公開済み記事の下書き行の2件が認証なしで取得できました。公開APIに下書きを出したくない場合は、コントローラーで status を上書きするなどの対策が要ります。
自動変換後にも確認が必要な破壊的変更
公式の破壊的変更一覧には、codemodが「No」の項目が多く並びます。管理画面の独自拡張を除き、APIを使う側で影響が出やすいものを選びました。
| 変更 | v4での挙動 | v5での挙動 | codemod |
|---|---|---|---|
| REST入力の検証 | 未知の項目は黙って除去 | 400 ValidationError | なし |
| 属性名 status | D&P有効でも使用可 | D&P有効の型では予約語 | なし |
| 作成時のファイル送信 | エントリー作成と同時に可 | Upload APIで先に登録 | なし |
| 動的ゾーンのpopulate | 全コンポーネント共通の指定可 | 型ごとの指定は on を使う | なし |
| id順の並べ替え | 作成順になる | createdAtで並べる | あり |
| 会員登録の追加項目 | allowedFields未設定で全項目可 | 未設定は空配列=不可 | あり |
| 環境変数だけの設定 | STRAPI_HIDE_STARTUP_MESSAGE等 | config/server.tsへ | なし |
最初の2つは5.58.0の移行後の環境で再現しました。スキーマにない項目 foo を付けて POST /api/articles を送ると、v4なら黙って捨てられていた項目で 400 と "message":"Invalid key foo" が返り、記事は作られませんでした。フォームの値をそのまま送っているフロントエンドや、外部サービスからのWebhookを受けて記事を作る処理は、送る項目をスキーマに合わせて絞ります。なお、RESTの POST に status を付けないと公開済みの記事として作られ、下書き行も同時にできました。サーバー側の documents().create() が既定で下書きを作るのとは逆です。
記事の型に文字列の status 属性を足して起動すると、「The attribute name ‘status’ on content type ‘api::article.article’ is reserved when ‘draftAndPublish’ is enabled.」という警告が1行出たうえで、起動自体は続きました。止まらないので見落としやすく、そのまま使うとクエリの ?status= と自前の属性がぶつかります。v4で status という名前の項目(承認状態など)を持っていた型は、approval_status などに改名してから移行します。
一覧で populate=* を使う1階層の取得や通常のコンポーネントの指定は、v5でもそのまま使えます。フォームから画像付きで記事を投稿させている構成は、POST /api/upload でファイルを登録し、返ったIDを付けて記事を作る2段階に書き換えます。会員登録で氏名などの独自項目を受けている場合は、config/plugins.ts の users-permissions.config.register.allowedFields に項目名を並べます。username・email・password の3つは設定しなくても常に受け付けられます。
ライフサイクルフックの発火の違いとDocument Service middlewareへの移行
upgradeツールが lifecycles.ts をコメントアウトするのは、v5ではフックの発火の仕方が変わり、そのまま戻すと意図しない処理が走るからです。v4と同じフック(beforeCreate・afterCreate・afterUpdate・afterDelete)を有効に戻し、Document Service APIの各操作で何が発火するかを5.58.0で数えました。
| Document Service APIの操作 | middlewareのaction | 発火したafter系フック |
|---|---|---|
| create(下書き) | create | afterCreate 1回 |
| update | update | afterUpdate 1回(下書き行) |
| publish(初回) | publish | afterCreate 1回(公開行を作成) |
| publish(2回目以降) | publish | afterDelete 1回 → afterCreate 1回 |
| unpublish | unpublish | afterDelete 1回 |
| create(status: ‘published’) | create | afterCreate 2回 |
| delete(公開済みの記事) | delete | afterDelete 2回 |
2026年9月30日に5.55.1で数えた結果と、回数・順序とも同じでした(比較したのは5.55.1と5.58.0の2版で、間のマイナー版は数えていません)。
公開は「公開行を作る」、再公開は「古い公開行を消して作り直す」、公開取り消しは「公開行を消す」という行単位の操作として実行されます。v4で「記事が作成されたらSlackに通知する」処理を afterCreate に書いていた場合、v5では公開のたびに通知が飛び、作成と同時に公開すると2通届きます。検索インデックスを afterDelete で消していた処理は、再公開のたびに一瞬インデックスから消えます。
1回の操作を1回として扱いたい処理は、Document Service middlewareに移します。middlewareは上の表のとおり、どの操作でも1回だけ呼ばれ、context.action で操作の種類を判別できます。
export default {
register({ strapi }) {
strapi.documents.use(async (context, next) => {
const result = await next();
if (context.uid === 'api::article.article' && context.action === 'publish') {
if (Array.isArray(result?.entries) && result.entries.length > 0) {
await notifyPublished(result);
}
}
return result;
});
},
};
middlewareは src/index.ts の register() に書きます。notifyPublished は通知処理を想定した自作関数です。この例は publish() の明示的な呼び出しを対象とし、例外がなく、公開結果の entries が空でない場合に通知します。create()・update() に status: 'published' を渡す公開も通知対象にする場合は、それぞれのactionと戻り値に対応した分岐が必要です。
フロントエンドの段階移行:Strapi-Response-Format: v4とv4CompatibilityMode
RESTはStrapi-Response-Format: v4ヘッダーで旧形式を維持
リクエストヘッダーに Strapi-Response-Format: v4 を付けると、v5でも attributes の入れ子を持つ形式で返ります。移行後の環境で、記事にカテゴリーのリレーションを足して populate 付きで取得した結果が次です。
// v5(ヘッダーなし)GET /api/articles?populate=category
{"data":[{"id":10,"documentId":"teioh2kt2pgwvdpsq4q3rtdt","title":"Published in v4",
"category":{"id":2,"documentId":"zjmrd3hvwlqvtc2esceif64f","name":"News"}}], ...}
// v5(Strapi-Response-Format: v4)
{"data":[{"id":10,"documentId":"teioh2kt2pgwvdpsq4q3rtdt",
"attributes":{"title":"Published in v4",
"category":{"data":{"id":2,"documentId":"zjmrd3hvwlqvtc2esceif64f",
"attributes":{"name":"News"}}}}}], ...}
v5の形では、v4で item.attributes.category.data.attributes.name と4段たどっていた値が item.category.name になります。ヘッダーを付けると、記事本体だけでなくリレーション先の data と attributes の入れ子も戻りました。戻らないのは documentId の追加で、数値IDでの1件取得が404になる点もヘッダーでは変わりません。この記事の id は移行直後の1から10に変わっていました。カテゴリーを付けて公開し直したとき、公開行が消されて作り直されたためです。数値の id は公開のたびに変わりうるので、外部システムとの突き合わせには documentId を使います。キャッシュキーにはこれに加え、取得するロケールや公開状態も含め、同じDocumentの異なる版を区別します。
ヘッダーの提供期間について、Strapiのコラボレーターは2026年2月23日にGitHub Discussion #25524で「v5の間は維持し、削除されうる最も早い時期はv6」と回答しています。一覧表示から順にヘッダーを外し、エンドポイント単位でv5形式へ移していけます。
GraphQLはv4CompatibilityModeで旧スキーマを維持
// config/plugins.ts
export default {
graphql: {
config: {
v4CompatibilityMode: true,
},
},
};
互換モードでは data.attributes.* の形でクエリを書き続けられます。そのうえで、id を documentId に置き換え、一覧のクエリを articles_connection のような _connection 付きの名前に移し、ページ情報を meta.pagination から pageInfo へ読み替えます。すべてのクエリがv5の形で動いたら互換モードを外します。ファイルアップロード用の uploadFile などのミューテーションは削除されたため、アップロードはRESTで行います。GraphQLそのものの考え方はGraphQLとは?REST APIとの違い・メリット・デメリットと最小実装をコード付きで解説を参照してください。
データベースとSQLiteの変更点
- MySQLは8.0以上が必要で、MySQL 5は非対応になりました。ドライバーは
mysql2だけが使えます。 - SQLiteのドライバーは
better-sqlite3だけになり、設定のclientはsqliteと書きます。今回のプロジェクトはv4の時点でbetter-sqlite3を使っていたため、版が上がっただけでした。 - 55文字を超えるテーブル名・カラム名は自動で短縮されます。長いコンポーネント名を使っていて、外部からDBを直接読むツールがある場合は、移行後の名前を確認します。
- Draft & Publishを無効にした型でも、
publishedAtには作成・更新日時が入るようになりました。publishedAtが空かどうかで判定していた処理は結果が変わります。
v4に残る場合のリスクと移行を急ぐべき構成
v4のサポートは2026年4月30日に終了しています。最終版の4.26.2はその後の2026年6月9日に出ており、4.26.2のリリースノートにはリレーションを使った絞り込みで機密データが漏れうる重大な脆弱性の修正が含まれています。リリースノートは「今後v4の更新は出さない」と明言しているため、次に脆弱性が見つかっても修正は届きません。
インターネットに公開したAPIをv4で動かしているなら、移行は後回しにすべきではありません。少なくとも4.26.2へ上げたうえで、v5への移行計画を立てます。Strapi Cloudについては、v4のプロジェクトは引き続き動くものの予告なく変わる可能性があると、公式の移行FAQが書いています。
移行の工数は、管理画面の独自拡張と、作成・公開をきっかけに動く処理の量で決まります。管理画面が標準のままで、フロントエンドが一覧と詳細を取るだけの構成なら、今回のようにcodemodと数か所の修正で移れます。helper-plugin に依存した独自プラグインや、ライフサイクルフックでの外部連携が多い構成は、テスト環境で上の表と同じ発火回数の確認をしてから本番に進めます。v5への移行を機にCMSを変えることも考えるなら、TypeScriptでスキーマを定義するPayload CMSとは?使い方・Next.js統合・Strapiとの違いを解説【v3.90・4.0 beta】や、SaaS型のContentfulとは?API構成と料金の増え方・移行手順を実コードで解説が比較対象になります。CMSの選定から構築・移行まで外部に任せる場合は、issohのWordPressサイト制作・CMS構築でも相談を受けています。
Strapi v5に関するよくある質問
Strapi v5の最新バージョンはいくつですか?
2026年10月19日時点のnpmの最新は5.58.0(2026年10月14日公開)です。5.0.0は2024年9月18日に公開され、その後は5.x系のマイナー版がほぼ毎週出ています。直近では5.57.0でContent-type Builderの属性名変更時にデータが残るようになり、5.58.0で管理画面にライセンス詳細の表示が加わりました。動作要件の詳細はStrapiの総合解説記事にまとめています。
v5へ移行したあとでv4に戻せますか?
自動で戻す手段はありません。v5で一度起動するとDBの移行スクリプトが走り、行の構成が変わります。戻す場合は、移行前に取ったDBのバックアップを復元し、コードもv4のブランチに戻して、package.json とロックファイルに合わせて依存を入れ直します。移行作業の前にバックアップを取る理由はここにあります。
Strapi v5でも管理画面を日本語にできますか?
できます。src/admin/app.example.tsx を app.tsx に改名し、config.locales に 'ja' を加えると、管理画面の言語に日本語が選べるようになります。
Strapi v6はいつ出ますか?
公式の発表はありません。開発チームはGitHub Discussion #25117で、2026年中はメジャー版を出さない方針を示しています。v5へ移行したプロジェクトは、当面はマイナー版の更新だけで済む見込みです。
Strapi Cloudで動いているv4のプロジェクトはどう移行しますか?
コードの移行はセルフホストと同じく npx @strapi/upgrade major で行います。更新したコードをCloud CLIの npm run deploy でデプロイすると、Cloud側でデータ移行スクリプトが自動で実行されます。公式手順は、Cloudでもデプロイ前に手動でバックアップを取るよう案内しています。