データ取得
管理された TanStack Query(useQuery)でサーバー状態を扱う。
Vantage はサーバー状態のために 管理された TanStack Query を同梱しています。QueryClient の
セットアップは不要で、@squadbase/vantage/query から useQuery などを import するだけです。
自分の server/api を呼ぶなら、後述の useApiQuery が近道です。
import { useQuery } from "@squadbase/vantage/query";
export default function Customers() {
const { data, isLoading, error } = useQuery({
queryKey: ["customers"],
queryFn: async () => {
const res = await fetch("/api/customers");
if (!res.ok) throw new Error("failed to load");
return res.json();
},
});
if (isLoading) return <p>読み込み中…</p>;
if (error) return <p>エラーが発生しました</p>;
return (
<ul>
{data.map((c: { id: string; name: string }) => (
<li key={c.id}>{c.name}</li>
))}
</ul>
);
}
単一の管理された QueryClient
Vantage が用意する QueryClient は 1 つだけで、次のデフォルトを持ちます。
| 設定 | 値 |
|---|---|
staleTime |
30 秒 |
retry |
1 |
refetchOnWindowFocus |
false |
networkMode |
"always"(queries / mutations とも) |
networkMode: "always" は、ブラウザのオフライン判定を無視して常にリクエストとリトライを行う
設定です。既定の "online" だと、オフラインと判定された瞬間に失敗したクエリが paused に
なり、エラーにならないまま読み込み表示が残り続けます。ダッシュボードは自分のオリジン(または
設定した API ベース)と話すので、その判定は当てになりません。
カスタマイズはクエリごとのオプションで上書きします。QueryClient 丸ごとの差し替えは、
管理されたスタックを保つため意図的に公開していません。
// このクエリだけ挙動を変える
useQuery({
queryKey: ["metrics"],
queryFn: fetchMetrics,
staleTime: 5 * 60 * 1000, // 5 分
retry: 3,
});
自分の API を呼ぶ — useApiQuery
server/api のルートを呼ぶときは useApiQuery を使います。useQuery に、毎回書いていた
定型(ベース URL の解決・JSON パース・非 2xx のエラー化・クエリキー)を足したものです。
import { useApiQuery } from "@squadbase/vantage/query";
interface Monthly {
monthly: { month: string; revenue: number }[];
}
export default function MonthlyAnalysis() {
const { data, isPending, error } = useApiQuery<Monthly>("/api/monthly-analysis");
if (isPending) return <p>読み込み中…</p>;
if (error) return <p>{error.message}</p>;
return <Chart data={data.monthly} />;
}
クエリ文字列は search で渡します。undefined の項目は落ちるので、未設定のフィルタは
URL にもクエリキーにも現れません。
const query = useApiQuery<Row[]>("/api/customers", {
search: { segment, from: range.from, to: range.to },
enabled: Boolean(segment), // useQuery のオプションはそのまま使える
});
クエリキーは既定で ["api", <クエリ文字列込みの URL>] です。したがって search が変われば
再取得され、invalidateQueries({ queryKey: ["api"] }) で API 由来のキャッシュを一括で捨てられます。
エラーは ApiError
非 2xx のレスポンスは ApiError として throw されます。message は API が
HttpError で投げたメッセージ、それ以外は汎用のステータス文言です。
| プロパティ | 内容 |
|---|---|
message |
サーバーが HttpError で明示したメッセージ |
status |
HTTP ステータス |
body |
パース済みのレスポンスボディ |
requestId |
x-vantage-request-id。開発ターミナルのログと突き合わせられる |
書き込みは useApiMutation
変数はそのまま JSON ボディとして送られます。パスは関数でも渡せます。
import { useApiMutation, useQueryClient } from "@squadbase/vantage/query";
const queryClient = useQueryClient();
const save = useApiMutation<Customer, { id: string; name: string }>(
(vars) => `/api/customers/${vars.id}`,
{
method: "PUT",
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["api"] }),
},
);
save.mutate({ id: "1", name: "Northwind" });
hook が使えない場所(イベントハンドラなど)では apiJson(path, init) を直接呼べます。
生の Response が欲しい場合は apiFetch です。
次に読む
- API とサーバー — バックエンドを書く