Next.js App Routerのキャッシュのメモ
参考文献
mugiさんの記事と動画で勉強させていただく、、!
動画
記事
キャッシュは4種類ある
今回検証するのは「Request Memorization」と「Data Cache」
準備をする
expressのAPI用のサーバーを用意する
const express = require("express");
const app = express();
const port = 5555;
app.use(express.json());
let getCounter = 0;
app.get('/', (req,res)=>{
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
console.log(`==== get ${++getCounter} =====`)
res.json({data: `${new Date().getTime()}`})
})
let postCounter = 0;
app.post('/', async(req,res)=>{
const data = await req.body
console.log(data)
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
console.log(`==== post ${++postCounter} =====`)
res.json({data: `${new Date().getTime()}`})
})
app.listen(port, ()=>{
console.log(`Server is running on port ${port}`)
})
サーバーを起動させておく
http://localhost:5555/
叩いたら、以下が返ってくる(GETメソッド)
{"data": "1708153301754"}
Fetch & GET
export const API_URL = "http://localhost:5555";
import { Child } from "./Child";
import { API_URL } from "./config";
export default async function Page({}: {}) {
const res1 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const res2 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const result1 = await res1.json();
const result2 = await res2.json();
return (
<main>
<h1 className="text-[50px]">fetch</h1>
<div className="text-red-500">
<p>{result1.data}</p>
<p>{result2.data}</p>
</div>
<Child />
</main>
);
}
import { API_URL } from "./config";
export async function Child({}: {}) {
const res1 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const res2 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const result1 = await res1.json();
const result2 = await res2.json();
return (
<main>
<div className="text-blue-500">
<p>{result1.data}</p>
<p>{result2.data}</p>
</div>
</main>
);
}
サーバー起動で、ブラウザ閲覧
http://localhost:3000/fetch
Automatic fetch() Request Deduping
という機能のおかげて、
全部で4回コールしているのに(ページで2回+子供コンポーネントで2回)
全部同じ値を流用している。
何回リロードしても変わらない。
API側も一度しかコールされていない。
キャッシュが使われているみたい。
APIサーバーは検証の度に再起動することをお勧めします。
パラメータを1個だけ変えた
const res1 = await fetch(API_URL + "?a=1", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
変えたやつだけ、変わった。
当たり前か。
ただ、また何度リロードしても同じ値。
デフォルトだと、パラメータなどを変えない限り、キャッシュは破棄されないみたい
revalidatePathを追加
export default async function Page({}: {}) {
const res1 = await fetch(API_URL + "?a=1", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const res2 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const result1 = await res1.json();
const result2 = await res2.json();
revalidatePath("/fetch");
return (
<main>
<h1 className="text-[50px]">fetch</h1>
<div className="text-red-500">
<p>{result1.data}</p>
<p>{result2.data}</p>
</div>
<Child />
</main>
);
}
それぞれ、独立した値になる & リロード度に新しい値になる。
1リロードでAPIも4回叩かれている。毎回。
キャッシュを使っていないようだ。
今度はrevalidate
のテスト。
urlは全部同じにする。
4つの関数コールで、revalidate
の値を独立させる
import { Child } from "./Child";
import { API_URL } from "./config";
export default async function Page({}: {}) {
const res1 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
next: { revalidate: 10 },
});
const res2 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
next: { revalidate: 5 },
});
const result1 = await res1.json();
const result2 = await res2.json();
return (
<main>
<h1 className="text-[50px]">fetch</h1>
<div className="text-red-500">
<p>{result1.data}</p>
<p>{result2.data}</p>
</div>
<Child />
</main>
);
}
import { API_URL } from "./config";
export async function Child({}: {}) {
const res1 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
next: { revalidate: 100 },
});
const res2 = await fetch(API_URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
next: { revalidate: 25 },
});
const result1 = await res1.json();
const result2 = await res2.json();
return (
<main>
<div className="text-blue-500">
<p>{result1.data}</p>
<p>{result2.data}</p>
</div>
</main>
);
}
リロードしまくる。
それぞれの呼び出し結果は、同じ=> fetchのキャッシュのため。
(下の図だと時間がよくわかりませんが、リロードしまくってます。)
今度は5秒に1度、データが更新される
APIも5秒に1回コールされている。
つまり一番小さい、これが採用されている。
next: { revalidate: 5 },
next: { revalidate: 5 },
next: { revalidate: 0 },
next: { revalidate: 100 },
next: { revalidate: 25 },
1個だけ、revalidate
を0にしたら、それだけ、リロード毎に更新されて、
あとはその次に短い5秒で統一されるっぽい。
ただ、ちょっと挙動があやしい。
基本、同じURLで、revalidate
の数字を変える等はやらない方がいい。
Fetch & POST
今度は、postメソッド、APIはさっきと同じ。
import { Child } from "./Child";
import { API_URL } from "./config";
export default async function Page({}: {}) {
const res1 = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: "data",
}),
});
const res2 = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: "data",
}),
});
const result1 = await res1.json();
const result2 = await res2.json();
return (
<main>
<h1 className="text-[50px]">fetch</h1>
<div className="text-red-500">
<p>{result1.data}</p>
<p>{result2.data}</p>
</div>
<Child />
</main>
);
}
import { API_URL } from "./config";
export async function Child({}: {}) {
const res1 = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: "data",
}),
});
const res2 = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: "data",
}),
});
const result1 = await res1.json();
const result2 = await res2.json();
return (
<main>
<div className="text-blue-500">
<p>{result1.data}</p>
<p>{result2.data}</p>
</div>
</main>
);
}
POST
でも同じdataなら、
何回リロードしてもキャッシュで同じ値。
コールも1回
Automatic fetch() Request Deduping は GET でしか動作しないなど、一部制約があります。
って書いてあったけど、POSTでもリクエストが1回だな。
なぜだ。
それぞれのパラメータを独立
body: JSON.stringify({
data: "data1",
}),
body: JSON.stringify({
data: "data2",
}),
body: JSON.stringify({
data: "data3",
}),
body: JSON.stringify({
data: "data4",
}),
値はそれぞれ独立したが、何回リロードしても変わらない。(それぞれのパラメータでキャッシュ)
APIも4回コールされてるが、そのあとは何度リロードしても変化なし。
今度は、こんな感じ。
結果は皆さんお察しの通り。
body: JSON.stringify({
data: "data1",
}),
next: { revalidate: 2 },
body: JSON.stringify({
data: "data2",
}),
next: { revalidate: 4 },
body: JSON.stringify({
data: "data3",
}),
next: { revalidate: 6 },
body: JSON.stringify({
data: "data4",
}),
next: { revalidate: 8 },
- それぞれ、パラメータが違うので、独立したurlとしてキャッシュされる
- なので、それぞれ、2秒、4秒、6秒、8秒で更新される。
最初は、4つ呼ばれてるが、
そのあとは、data1
が一番短い間隔で、data4
が一番長い間隔で呼ばれている
最後。
パラメータを一緒にするが、
revalidate
は分ける。
body: JSON.stringify({
data: "data100",
}),
next: { revalidate: 2 },
body: JSON.stringify({
data: "data100",
}),
next: { revalidate: 4 },
body: JSON.stringify({
data: "data100",
}),
next: { revalidate: 6 },
body: JSON.stringify({
data: "data100",
}),
next: { revalidate: 8 },
同じ値でキャッシュされる。
リロードしまくったら、2秒間隔で更新された。
APIも2秒に1回叩かれた。
つまり、revalidate
は一番短いものが優先される。
この辺はGETとPOST で変わらないみたい。
自分なりの結論
- fetch を使えば、GETでもPOSTでもキャッシュを使ってAPIへの接続を減らせる。
- 同じURL(パラメータ)毎にキャッシュは生成される(当たり前)
- しかもそのキャッシュは意図的にrevalidateしないと消えない。
- 例えば revalidateの設定が無い状態で、(A).
api/hoge?v=1
→ (B).api/hoge?v=2
→ (C).api/hoge?v=1
とすると(C)の時には(A)のキャッシュが再利用されるっぽい。
今回思ったのは、
API毎にfetchにキャッシュさせないか、時間単位でキャッシュさせる設定を指定する
のがベストだと思います。
next: { revalidate: 0 }, // キャッシュさせない
or
next: { revalidate: 10 }, // 10秒キャッシュさせる
こうする事で、他の設定に上書きされることはないからです。
(同じURLのfetchが他に存在しない限り、、)
設定が難解すぎるので、明示的に設定しといた方がいいかと。。
間違っていたら、コメントください。