最近フロントエンド開発のデータfetchの際よく見かける、SWRについて、実際に試して見ました。
https://swr.vercel.app/ja
上記より引用
“SWR” という名前は、 HTTP RFC 5861(opens in a new tab) で提唱された HTTP キャッシュ無効化戦略である stale-while-revalidate に由来しています。 SWR は、まずキャッシュからデータを返し(stale)、次にフェッチリクエストを送り(revalidate)、最後に最新のデータを持ってくるという戦略です。
上記「まずキャッシュからデータを返し」という部分が重要です。その後fetchしキャッシュを最新にします。
これを実現するためには、サーバのレスポンスヘッダCache-Controlにstale-while-revalidateを追加する必要があります。
https://nextjs.org/docs/app/building-your-application/caching#time-based-revalidation
下記は、stale-while-revalidateのタイムアウト時、リクエストの結果、ブラウザに表示された時間と、実際にサーバから返された時間が違っていることを示しています。キャッシュからまずデータが返され、その後サーバにfetchしにいっていることが確認できます。
環境) Mac(arm64)
sudo tcpdump -i lo0 port 8000 -n -A
下記テストに使ったコードです。
サーバはpython fastapi、クライアントは、next.js app router / typescript (layout.tsxは省略)
main.py
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35  | 
						import datetime from fastapi import FastAPI from fastapi.staticfiles import StaticFiles import json from fastapi import Response from fastapi.middleware.cors import CORSMiddleware origins = {     "http://localhost:3000", } app = FastAPI() app.mount("/static", StaticFiles(directory="static"), name="static") app.add_middleware(     CORSMiddleware,     allow_origins=origins,     allow_credentials=True,     allow_methods=["*"],     allow_headers=["*"], ) @app.get("/") def read_root(response: Response):     response.headers["Cache-Control"] = "max-age=10, stale-while-revalidate=10"     #response.headers["Cache-Control"] = "max-age=10"     ut = datetime.datetime.now()     print(ut)     tm = f'{ut}'     print(tm)     str = '{"hello":"'+tm+'"}'     print(str)     return json.loads(str)  | 
					
起動
uvicorn main:app –reload
page.tsx
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29  | 
						"use client" import React, { useState } from 'react' const getItem = async () => {   const data = await fetch('http://localhost:8000/', {next: {revalidate:20}})   const res = await data.json()   return res } const fetch01 = () => {   const [value, setValue] = useState()   return (     <>     <div>fetch test</div>     <div>{value?.hello}</div>     <button class='border' onClick={()=>{         getItem().then(res => {           setValue(res)         console.log(res)         })     }}>fetch</button>     </>   ) } export default fetch01  | 
					
起動
npm run dev
stale-while-revalidateを削除したり max-age、revalidateの値を変えたりして、実際に動かしてみると理解が深まります。

