🐡

Next.jsのServer ActionsではCookieを書き換えるとrevalidateが実行されるっぽい

2024/01/18に公開
1

表題の通りではあるんですが、Next.jsのServer ActionsではCookieを書き換えるとrevalidateが実行されるっぽい、という話です。

前提

  • Next.js 14.0.4

先に結論

Next.jsでServer Actionsを利用したアプリケーションを作っていると、revalidateが実行されて画面の再描画が走るケースと走らないケースがあることに気がつきました。

いずれの場合も、 revalidatePathrevalidateTags といった、キャッシュを破棄するための実装は行っていないにも関わらずです。なぜこのようなことが起きるのかを調査したところ、タイトルにもある通りですが「 Cookieの内容が変わっているとrevalidateが実行される 」ということがわかりました。

サンプルコード

https://github.com/uutarou10/rsc-revalidate-test

import {cookies} from "next/headers";

export default function Home() {
  const rewriteCookieAction = async () => {
    'use server'
    console.log('Called rewriteCookieAction')
    cookies().set('render-time', Date.now().toString())
  }

  const normalAction = async () => {
    'use server'
    console.log('Called normalAction')
  }
  return (
    <main>
      <h1>Revalidate behavior sandbox</h1>
      <div>Rendered: {new Date().toLocaleString()}</div>

      {/* SubmitするとCookieに現在のtimestampをsetするactionが実行される */}
      <form action={rewriteCookieAction}>
        <button type={"submit"}>Submit actions (rewrite cookie)</button>
      </form>

      {/* Submitするとconsoleにログを吐くだけのactionが実行される */}
      <form action={normalAction}>
        <button type={"submit"}>Submit actions</button>
      </form>
    </main>
  )
}

簡易的ですがこのようなページを用意してみました。二つのボタンがあり、前者は実行するとCookieにその時点のtimestampを焼きます。

これを実行してそれぞれのボタンを押してみると、前者のボタンを押した時は再描画が走り、Renderedの値が更新されます。一方で後者のボタンを押した時は再描画が走りません。

どうしてこうなるのか

それぞれのボタンを押した際のリクエストをみてみると、 x-action-revalidated というレスポンスヘッダの内容に差があることに気がつきました。

Cookieを書き換えた場合の内容は、 [[],0,1] となっており、通常のアクションを実行した際は [[],0,0] となっていました。

Next.jsのコードを見てみると、以下のような箇所がありました。

https://github.com/vercel/next.js/blob/486567d2d7eebb813c1ef6248a83f0dcae916771/packages/next/src/server/app-render/action-handler.ts#L107-L145

コメントと実装によれば、このヘッダーはrevalidateの挙動をクライアント側に伝えるためにあり、配列の一つ目の値がrevalidatePathの値、二つ目がrevalidateTagsの値、そして3つ目がCookieが変わったかどうかを表しているようです。(0 or 1)

おわりに

この挙動を知らずに結構な時間を溶かしてしまったので、何かの役に立てば幸いです。(自分の知る限りでは、今日時点でこの点についてはドキュメントに記載がないと認識しているのですが、もしあれば教えていただけますと幸いです。)

追記: 公式ドキュメントInvalidation の項目に Using cookies.set or cookies.delete invalidates the Router Cache to prevent routes that use cookies from becoming stale (e.g. authentication). とバッチリ記載されていました。大変失礼しました。App Routerのキャッシュ周りの挙動はしっかりと理解した上で開発をし始めないといけないと認識しました……。

余談ですが、今新規開発しているプロダクトでServer Actions/Server Componentsを使っており、加えてAuth0を利用しているため、ServerActionsの処理に getSession のような処理を呼ぶと、Cookieが書き換えられるため、結果として毎回意図せずにrevalidateが走ってしまうという問題に直面しています。

諸々の仕様を理解した上で使わないと、何が起因してrevalidateを引き起こしているのか判断がつかなく時間を溶かすことになってしまうと改めて感じました。