最近フロントエンド開発のデータ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の値を変えたりして、実際に動かしてみると理解が深まります。