【新機能】Vercel KVで遅いAPIレスポンスをキャッシュする
こんにちは
昨日(5/1深夜)のVercel Shipで、Vercel KV, Vercel Postgress, Vercel Blob とストレージに関する三つの新機能が発表されました。
昨今のフロントエンド情勢ではCDNを使ったキャッシュ戦略など、低コストに大規模なトラフィックを捌く需要が高まっており、今までVercelでサポートが薄かったストレージ周りが、気軽に使えるようになるのはかなり嬉しいです。
今回は、発表された三つの新機能の中から、Vercel KVについて、簡単にキャッシュ用途で試して見たので記事に書いてみます。
KVって何?
key Valueの略称で、名前の通りキーと値のペアを保存することができます。
Vercel KVは、中身はRedisデータベースのようなものになっていて、キーとJSONのペアなどをエッジ上で扱うことができます。
Vercel KVを試す
Vercel KV
を使ったなるべくシンプルな例として、遅い外部APIのレスポンスをキャッシュする用途で使って、サンプルアプリを作成します。
プロジェクトはNext.js
のappDir
を使用して作成します。
サンプルアプリのゴール
アクセスしたユーザー名を含むパスに対して挨拶を返す遅いAPIがあります。このAPIレスポンスに基づいて表示される画面を、KVを用いて2回目以降早く表示されるようにします。
KVの用途として、セッションの管理など様々あるのですが、KVを一番シンプルに使えそうな例を考えてキャッシュとしました。
リポジトリ
以下のリンクにデプロイしてあるので、試してみることもできます。
URLのpiを好きな文字列に書き換えると挨拶をしてくれます。
プロジェクトを作る
npx create-next-app
でプロジェクトを作ります
✔ What is your project named? … vercel-kv-playground
✔ Would you like to use TypeScript with this project? … Yes
✔ Would you like to use ESLint with this project? … Yes
✔ Would you like to use `src/` directory with this project? … No
✔ Would you like to use experimental `app/` directory with this project? … Yes
一旦VercelにデプロイしてKVを接続する
一旦この状態でデプロイして、Vercelのプロジェクトを作成します。
プロジェクトを作成したらStorage
タブのCreate
からKV
を作成します。
ダイアログを開くので、Create New
を押して続けます。
次にDBの名前と、リージョンを選択します。KVが使えるリージョンは一部で日本リージョンはありません。KV
では1箇所の書き込み用リージョンと、4箇所の読み込み用リージョンを用意できるので、利用元から近いリージョンを適時選択してください。
最後にプロジェクトと接続すると、そのプロジェクトでKV
を使うことができます。
プロジェクトに接続までできると、ダッシュボードで設定するべき環境変数をみることができます。
これを.env.local
に貼り付けるとローカルでもVercel KV
を使うことができます。
レスポンスが遅いAPIを用意する
開発の準備が整ったので、クエリパラメータで受け取った名前に対して、挨拶を返すだけの簡単なAPIを用意します。
このAPIがリクエストに時間がかかるものとして、3秒間待ってからレスポンスを返すようにしました。
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const name = searchParams.get("name");
const data = { message: `Hello ${name}!` };
await new Promise((resolve) => setTimeout(resolve, 3000));
return NextResponse.json(data);
}
名前を含んだURLから挨拶を表示する
/greeting/{name}
にアクセスした時に、挨拶を表示するページを用意します。
このページはURLに含まれる名前をSayHello
コンポーネントに渡すだけです。
import SayHello from "@/components/say-hello";
import { Suspense } from "react";
export const dynamic = "force-dynamic";
export default function Page({ params }: { params: { name: string } }) {
return (
<main>
<Suspense>
{/* @ts-expect-error Async Server Component */}
<SayHello name={params.name} />
</Suspense>
</main>
);
}
SayHelloコンポーネントではAPIを叩いてユーザーに表示するメッセージを取得、表示します。
export default async function SayHello({ name }: { name: string }) {
const res = await fetch(
`https://vercel-kv-playground.vercel.app/api/greeting?name=${name}`
);
const { message } = await res.json();
return <p>{message}</p>;
}
これで、/greeting/eito
のようにアクセスすると、3秒ほどでHello eito!
と挨拶が返ってくると思います。
このままだと表示が遅いので、KVを使って改善します。
KVでレスポンスをキャッシュする
先ほどの例で挨拶が表示されるようになったのですが、APIレスポンスを待たなきゃいけなくて表示が遅いです。
KVにAPIレスポンスを保存して、再利用できるようにしてみます。
まずvercel/kv
からkv
をインポートします。
import kv from "@vercel/kv";
KV
はキーと値のペアになるので、名前から保存する時のキーを生成する関数を作ります。
function kvKey(name: string) {
return `${name}-key`;
}
APIレスポンスを書き込んだり読み込んだりする時のキーはこの関数を使います。
次に、APIレスポンスをKV
に書き込みます。
await kv.json.set(kvKey(name), "$", { message });
kv.json.get
などのメソッドの使い方は、Redisコマンドのドキュメントが参考になります。
vercel/kv
のドキュメントに例が書いていないようなコマンドは、こちらを見るとわかりやすいと思います。
KV
から読み取る。
const kvValue = await kv.json.get(kvKey(name), "$");
値があればkvValue
が配列で返ってくるので、これを使うようにすればOKです。
コードの全体は以下のようになりました。
import kv from "@vercel/kv";
export default async function SayHello({ name }: { name: string }) {
const kvValue = await kv.json.get(kvKey(name), "$");
if (kvValue) {
return <p>{kvValue[0].message} from kv</p>;
}
const res = await fetch(
`https://vercel-kv-playground.vercel.app/api/greeting?name=${name}`
);
const { message } = await res.json();
await kv.json.set(kvKey(name), "$", { message });
return <p>{message} from api</p>;
}
function kvKey(name: string) {
return `${name}-key`;
}
これで2回目以降のアクセスでは早く表示されるようになったので、目標は達成です🙌
最後に
Vercel KV
、Redis
互換でサポートしてるAPIが多いおかげでシンプルにJSONを扱えたりして便利でした。
ストレージ周りの大幅強化からの、まだまだ発表が続くみたいなので今後も楽しみです!
Discussion