LoginSignup
4
5

はじめに

Qiita/RSSシリーズです。前回までの記事を振り返ります。

記事①では…。

「Qiita/RSS」を題材にPHPでXMLの解析を行いました。

記事②では…。

「Qiita/RSS」を題材にDrupalでAPI化をしてみました。

今回は…。

タイトルでも紹介している通り、Next.js で Drupal で準備した API に Fetch してみます!!(お前フロント行けるんかい 😥)

行けないです。ストリート実装です。
夏休みの自由研究程度で見てください。

一応…。完成イメージ。
umekikazuya.vercel.app

さっそくですが、実装の紹介をします。

Next.js ですが、AppRouter で実装しました。話題の TurboPack とやらも試してみました。

Fetch実装

公式の DataFetch の Document を参考に実装してみました。

/utils/api.ts
export async function fetchData<T>(url: string): Promise<T | null> {
  try {
    const res = await fetch(url);
    if (!res.ok) {
      throw new Error("No data.");
    }
    return res.json();
  } catch (error) {
    return null;
  }
}
// 本記事では例外処理割愛.

<T>
同僚のフロントエンドエンジニアに教えてもらいました。Generics 便利。

レンダリング実装

/app/page.tsx
export default async function Page() {
  const feed = await loader();

  return (
    {feed && <Feed feed={feed} />}
  );
}

async function loader() {
  const endpoint = `${process.env.NEXT_DRUPAL_ENDPOINT}${process.env.NEXT_PUBLIC_QITIA_ID}`;
  const feed = await fetchData<FeedType>(endpoint);
  return feed;
}
// 本記事では例外処理割愛.

環境変数でエンドポイントを指定して Fetch します。
レンダリングはあえて別Componentに任せました。

Component実装

/components/feed.tsx
export function feed({ feed }: FeedProps) {
	return (
    <>
      <ul>
        {feed.data.map((item, index) => (
          <ListItem
            key={index}
            title={item.title}
            link={item.link}
            published={item.published}
          />
        ))}
      </ul>
    </>
  );
}

function ListItem({ title, link, published }: FeedElement) {
  return (
    <li className={style.li}>
      <a className={style.li_inner} href={link} target="_blank" rel="noreferrer">
        <span className={style.li_inner__title}>{title}</span>
        <time dateTime={published} className={style.li_inner__time}>
          {new Date(published).toLocaleDateString("ja-JP")}
        </time>
      </a>
    </li>
  );
}
// `new Date()`等の例外処理は本記事では割愛.

使いまわし想定はないので、あんまComponent化する意味があるかと言われると微妙ですが…。

「コードの責務」を考慮した時に、「Fetch」「配列操作」「レンダリング」って意図で分割したのは後から見た時にわかりやすいかなと思ってやっています。(試行錯誤中)

まぁこんな感じですよね。夏休みの自由研究にしては上出来…と思っときます。

詰まったポイントを共有します。

環境変数の扱い方

「サーバーサイドコンポーネント」と「クライアントサイドコンポーネント」で、環境変数の扱い方が異なるようです。
ちょっとハマりました。

変数をブラウザに公開するには、変数の前に NEXT_PUBLIC_ を付ける必要があります。

クライアントサイドで使用したい時は prefix が必要なんですね。

データのキャッシュが永続的??

Fetch側は設定なしの場合都度通信してくれるのかと思ったらそうでもないよう。

明示的に設定する必要があるようです。

  • Time-based revalidation:時間駆動
  • On-demand revalidation:イベント駆動
fetch('https://...', { next: { revalidate: 3600 } })

今回は、時間駆動で設定しましたが記事投稿時に何かしらのイベントを拾うように組み込むのもおもしろいかも。

子コンポーネントでFetchできない

CSRだったら問題なく行けた気がするんで同じ感覚でFetchしてたんですが、上手くいかない…

後から調べたんですが、今回のようなFetchの方法では page.tsx のみでしかデータのfetchできないようです。

「ServerSideRenderingなのに子要素でFetchするって、設計的にしっくりこないんで当たり前なのかな…。」って思うようにします。

まとめ

Backend の API(Drupal)側でキャッシュ機構はしっかり組み込んだので、Frontend側の細かいキャッシュのチューニングについては記事内では省きました。

Next.jsのDataFetchには標準でCache機構が搭載されているようですので、どの程度扱いやすいかにもよりますが検討しようかなと思います。

Drupalだったら簡単に Headless できるんでぜひ試してみてくださいね。(本記事の内容は、データ自体の出力をしてないからHeadlessではないけど)

もちろん、Drupal内のData(Entity)に外部からアクセスすることも可能です。

実は、まだこのシリーズ終わりません。(え〜!!?)
せっかく準備したAPIです。活かさないともったいないですよね。
Slack通知編いきましょう。(え〜!!?)

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5