🦁

Next.js for Drupal の BASE_PATH 問題と修正方法(patch-package活用)

2025/03/02に公開

概要

Next.js for Drupalのv2.0.0が2025/2/11にリリースされました。

https://next-drupal.org/

https://next-drupal.org/blog/next-drupal-2-0

早速試してみたところ、BASE_PATHの取り扱いについて対応が必要だったので、備忘録です。

環境変数

環境変数のサンプルは以下のようになっています。

.env.sample
# 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_URLhttps://site.example.com/xxxのようなベースパスを含めた形で指定すると、APIのリクエストはhttps://site.example.com/jsonapi/などに送られ、リソースを正しく取得できませんでした。

原因

エラーが発生しているgetResourceCollectionを確認したところ、問い合わせ先のURLを作成するbuildUrl関数において、new URL(path, this.baseUrl);が使用されていました。

node_modules/next-drupal/dist/index.js

...

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.jsonscripts に以下を追加
"scripts": {
  "postinstall": "patch-package"
}
  • node_modules/next-drupal/dist/index.js を直接編集(すでに変更済みの場合、このままでOK)
node_modules/next-drupal/dist/index.js
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