Next.js for Drupal の BASE_PATH 問題と修正方法(patch-package活用)
概要
Next.js for Drupalのv2.0.0が2025/2/11にリリースされました。
早速試してみたところ、BASE_PATHの取り扱いについて対応が必要だったので、備忘録です。
環境変数
環境変数のサンプルは以下のようになっています。
# See https://next-drupal.org/docs/environment-variables
# Required
NEXT_PUBLIC_DRUPAL_BASE_URL=https://site.example.com
NEXT_IMAGE_DOMAIN=site.example.com
# Authentication
DRUPAL_CLIENT_ID=Retrieve this from /admin/config/services/consumer
DRUPAL_CLIENT_SECRET=Retrieve this from /admin/config/services/consumer
# Required for On-demand Revalidation
DRUPAL_REVALIDATE_SECRET=Retrieve this from /admin/config/services/next
この時、NEXT_PUBLIC_DRUPAL_BASE_URL
にhttps://site.example.com/xxx
のようなベースパスを含めた形で指定すると、APIのリクエストはhttps://site.example.com/jsonapi/
などに送られ、リソースを正しく取得できませんでした。
原因
エラーが発生しているgetResourceCollection
を確認したところ、問い合わせ先のURLを作成するbuildUrl
関数において、new URL(path, this.baseUrl);
が使用されていました。
...
buildUrl(path, searchParams) {
const url = new URL(path, this.baseUrl);
const search = (
// Handle DrupalJsonApiParams objects.
searchParams && typeof searchParams === "object" && "getQueryObject" in searchParams ? searchParams.getQueryObject() : searchParams
);
if (search) {
url.search = stringify(search);
}
return url;
}
...
async buildEndpoint({
locale = "",
path = "",
searchParams
} = {}) {
const localeSegment = locale ? `/${locale}` : "";
if (path && !path.startsWith("/")) {
path = `/${path}`;
}
return this.buildUrl(
`${localeSegment}${this.apiPrefix}${path}`,
searchParams
).toString();
}
...
async getResourceCollection(type, options) {
options = {
withAuth: this.withAuth,
deserialize: true,
...options
};
const endpoint = await this.buildEndpoint({
locale: options?.locale !== options?.defaultLocale ? options.locale : void 0,
resourceType: type,
searchParams: options?.params
});
this.debug(`Fetching resource collection of type ${type}.`);
const response = await this.fetch(endpoint, {
withAuth: options.withAuth,
next: options.next,
cache: options.cache
});
await this.throwIfJsonErrors(
response,
"Error while fetching resource collection: "
);
const json = await response.json();
return options.deserialize ? this.deserialize(json) : json;
}
ChatGPTに質問したところ、以下の回答が得られました。
new URL(path, this.baseUrl) を使用すると、path が絶対パス(/jsonapi など)だった場合、baseUrl のホスト部分のみが適用され、/xxx などのパスプレフィックスが無視されてしまいます。そのため、APIリクエストが /jsonapi/ に送信され、本来の https://site.example.com/xxx/jsonapi/ には届かなくなります。
対策
ChatGPTにより、patch-package を使う方法が提案されました。以下、その回答です。
patch-package
を使うと、node_modules
内のファイルにパッチを適用し、変更を維持できます。
手順
- patch-package をインストール
npm install patch-package postinstall-postinstall
-
package.json
のscripts
に以下を追加
"scripts": {
"postinstall": "patch-package"
}
-
node_modules/next-drupal/dist/index.js
を直接編集(すでに変更済みの場合、このままでOK)
buildUrl(path, searchParams) {
const url = new URL("/xxx" + path, this.baseUrl); // 今回はハードコーディングにより修正
const search = (
// Handle DrupalJsonApiParams objects.
searchParams && typeof searchParams === "object" && "getQueryObject" in searchParams ? searchParams.getQueryObject() : searchParams
);
if (search) {
url.search = stringify(search);
}
return url;
}
- パッチを作成
npx patch-package next-drupal
patches/next-drupal+<version>.patch
というファイルが作成される。
- これで、
npm install
をしてもpostinstall
スクリプトが自動的にパッチを適用してくれる。
まとめ
上記の対策により、Next.js 15のApp RouterとDrupal 11で、やりとりを行うことができました。
他にも良い方法があるかと思いますが、参考になりましたら幸いです。
Discussion