コンテンツにスキップ
Vantage
English
Esc
navigateopen⌘Jpreview
このページの内容

Data fetching

Handle server state with a managed TanStack Query (useQuery).

Vantage ships a managed TanStack Query for server state. There is no QueryClient to set up — just import useQuery and friends from @squadbase/vantage/query. To call your own server/api, useApiQuery below is the shortcut.

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>Loading…</p>;
  if (error) return <p>Something went wrong</p>;
  return (
    <ul>
      {data.map((c: { id: string; name: string }) => (
        <li key={c.id}>{c.name}</li>
      ))}
    </ul>
  );
}

A single managed QueryClient

Vantage provides exactly one QueryClient with these defaults:

Option Value
staleTime 30 seconds
retry 1
refetchOnWindowFocus false
networkMode "always" (queries and mutations)

networkMode: "always" requests and retries regardless of the browser’s offline heuristic. Under the default "online", a failed query is paused the moment that heuristic reads offline — no error, just a loading state that never resolves. A dashboard talks to its own origin (or the configured API base), where that heuristic says nothing useful.

Customize by overriding per query. Swapping the whole QueryClient is intentionally not exposed, to keep the stack managed.

// Change behavior for this query only
useQuery({
  queryKey: ["metrics"],
  queryFn: fetchMetrics,
  staleTime: 5 * 60 * 1000, // 5 minutes
  retry: 3,
});

Call your own API — useApiQuery

For routes under server/api, use useApiQuery. It is useQuery plus the boilerplate every page was repeating: base-URL resolution, JSON parsing, non-2xx to an error, and a query key.

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>Loading…</p>;
  if (error) return <p>{error.message}</p>;
  return <Chart data={data.monthly} />;
}

Pass the query string as search. undefined entries are dropped, so an unset filter appears neither in the URL nor in the query key.

const query = useApiQuery<Row[]>("/api/customers", {
  search: { segment, from: range.from, to: range.to },
  enabled: Boolean(segment), // any useQuery option still works
});

The query key defaults to ["api", <url with query string>], so changing search refetches, and invalidateQueries({ queryKey: ["api"] }) drops everything that came from the API.

Errors arrive as ApiError

A non-2xx response is thrown as an ApiError. Its message is what the API chose to disclose with HttpError; anything else surfaces as generic status text.

Property Contents
message The message the server set with HttpError
status HTTP status
body Parsed response body
requestId x-vantage-request-id, to correlate with the dev terminal log

Writes with useApiMutation

The mutation variables are sent as the JSON body. The path may be a function.

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" });

Where a hook doesn’t fit (an event handler, say), call apiJson(path, init) directly. For the raw Response, use apiFetch.

このページは役に立ちましたか?