benjuwan
@benjuwan

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

【Next.js / vercel】開発環境では問題が無く、ビルド時にエラーも出ないが、デプロイ(本環境)でのデータフェッチが失敗する

解決したいこと

Next.js(App router)でヘッドレスCMS(WordPress)のサイトを制作しています。
デプロイ先はvercelで無料プラン(Hobby)です。

今回お教えいただきたいのは「デプロイ(本環境)でのデータフェッチが失敗する」理由についてです。
データフェッチの方法はRoute Handlersで行っています。

実は、開発環境では問題なくデータフェッチできてコンテンツが表示されており、ビルド時にエラーも発生しません。

しかし「デプロイするとデータフェッチに失敗してコンテンツが表示されない」という状況になっています。

エラー内容

vercelのログを確認すると、データフェッチしているAPIファイル(例:/app/posts)と、そのAPIを呼び出すコンポーネントファイルで403または500エラーが出ています。

エラー解消

この質問を立てた数日後に無事に解消できました。
恥ずかしながら原因は国外IPからのREST APIへのアクセス制限設定でした。
おそらくデプロイ先がvercelだったので上記制限に抵触したのだと思います。

今回、『Xサーバー』にホスティングしたWordPressサイトのデータをREST APIでフェッチしようとしていました。
そこで『Xサーバー』のサーバーパネルログイン後に[WordPressセキュリティ設定]ページに遷移し、[国外IPアクセス制限設定]タブの[REST APIアクセス制限]をOFFにすることでデータフェッチできるようになりました。

0001.jpg

詳細情報

WordPress REST APIを用いてデータフェッチしており、エンドポイントは独自の設定です。

以下の情報のようにWPGraphQLプラグインを用いて行う方法もあるかと思います。

参考記事:WordPressをヘッドレスCMSにしてNext.jsでブログを作成する方法とエラー対処法

しかし今回は、WPGraphQLを用いずにfunctions.phpに直接エンドポイントを指定しています。

参考記事:WP REST APIで独自エンドポイントのカスタマイズで投稿記事を全件取得できるようにする

技術構成

@types/node@20.11.28
@types/react-dom@18.2.22
@types/react@18.2.66
eslint-config-next@14.1.3
eslint@8.57.0
html-react-parser@5.1.9
jotai@2.8.0
next@14.1.3
react-dom@18.2.0
react@18.2.0
swiper@11.0.7
typescript@5.4.2

確認したこと

  • 環境変数の設定(vercelでの設定と開発ファイル両方)
  • 独自エンドポイントの設定
    • ブラウザバーに直接エンドポイントを打鍵し、200ステータスが返ってきて、データも表示されることを確認
    • CORSやサーバー設定(REAT APIへのアクセス制御など)の設定
  • Route Handlersの設定・コード記述や、呼び出し方法(使い方)について
Route Handlers や呼び出し方法のコード
src/app/api/posts/route.ts
export const dynamic = 'force-dynamic';

import { contentPostType } from "@/app/types/contentPostType";

export async function GET() {
    const fetchUrl: string = `${process.env.FETCH_URL}/posts`;
    const res: Response = await fetch(fetchUrl, {
        cache: 'no-store',
        headers: {
            'Content-Type': 'application/json',
            'Referer': 'https://vercelでのデプロイドメイン.vercel.app/'
        }
    });

    try {
        if (!res.ok) {
            console.error(`Response status: ${res.status}`);
            const responseText = await res.text();
            console.error(`Response text: ${responseText}`);
            throw new Error(`HTTP error! status: ${res.status}`); // vercel のログでは 403 と表示される
        }

        const data: contentPostType[] = await res.json();
        return Response.json(data, { status: 200 });
    } catch (error: any) {
        console.error('Fetch error:', error);
        return Response.json({ error: error.message, stack: error.stack }, { status: 500 });
    }
};
src/app/page.tsx
export const dynamic = 'force-dynamic';

import { GET } from "./api/posts/route"
import baseStyles from "./styles/page.module.css";
import Link from "next/link";
import { notFound } from "next/navigation";
import { contentPostType } from "./types/contentPostType";
import PostArchive from "./components/elements/PostArchive";

export default async function Home() {
  const res: Response = await GET();
  const postsFetchData_contentType: string | null = res.headers.get('content-type');

  const jsonResponse = JSON.stringify(res);
  console.log(`Response size: ${Buffer.byteLength(jsonResponse, 'utf8')} bytes`);

  try {
    if (
      postsFetchData_contentType &&
      !postsFetchData_contentType.includes('application/json')
    ) {
      console.error(`app/page.tsx --- Response status: ${res.status}`); // フロント側のログに 500 が表示される
      console.error(`app/page.tsx --- Response text: ${await res.text()}`);
      throw new Error("src/app/page.tsx - postsFetchData");
    }
  } catch (error) {
    console.error("An error occurred:", error);
  }

  if (res.status === 404 || res.status === 500) notFound();
  const postsData: contentPostType[] = await res.json();

  return (
    <main>
      <section className={baseStyles.sectionElm}>
        <h2 className={baseStyles.sectionHeading_2}>お知らせ</h2>
        <Link href={'/posts'}>お知らせ一覧へ</Link>
        <div className={baseStyles.infoBox}>
          <PostArchive props={{
            fetchData: postsData,
            archivePathName: 'posts'
          }} />
        </div>
      </section>
    </main>
  );
}

上記に問題ないことは確認できています。

個人的に怪しいと感じている部分

今回WordPress REST APIを用いて取得しているデータは100〜1,000件近くあり、それらを一括でデータフェッチするようにしています。

開発環境において各種データフェッチ時にターミナルへまれに以下の内容が表示されます。

fetch for over 2MB of data can not be cached ... 

つまり、取得しようとしているデータ量が多すぎる(大き過ぎる)のが原因なのでは?と。
vercel(の無料プラン)が課している何らかの制限を守っていない(超えている)ために「本環境でのデータフェッチ」に支障が出ているのでは?と感じています。

原因は上記の内容と関係なく、前述通り国外IPからのREST APIへのアクセス制限設定でした。

知りたいこと

ここまで長々と失礼いたしました。

現状、上記エラーで行き詰まったので別の方法(SSG)で実装を進めて完了しており、今回の質問内容を必ずクリアしなければならないという状況ではありません。

しかし、分からないことを分からないままにするのが気持ち悪かったので質問させていただきました。

上記状況もあって
今回知りたいことは具体的な解決策というよりも、
前述の個人的に怪しいと感じている部分を含めた「デプロイ(本環境)でのデータフェッチが失敗する」理由(または理由を知るための手がかり)となります。

正直「それはvercelに聞けよ」という質問内容だと自覚しているのですが、有識者の方から有益な情報が得られるかもしれないと質問させていただきました。

ここまで読んでいただき、ありがとうございます。
ご都合あえば(または気が向いたら)ぜひ回答いただけますとありがたく存じます。

0

No Answers yet.

Your answer might help someone💌