Data Fetching & State Management (TanStack Query)
Relevant source files
The following files were used as context for generating this wiki page:
TanStack Query is the primary library for managing asynchronous server state within the apps/web frontend application. As a core part of the project's tech stack, it provides powerful tools for fetching, caching, synchronizing, and updating server data, ensuring a modern and efficient user experience. It is deeply integrated with other parts of the TanStack ecosystem used in this project, such as TanStack Router.
This system works in conjunction with the backend api application, which exposes data via Hono and ORPC. The integration is designed to be type-safe, leveraging shared contracts to ensure consistency between the frontend and backend.
Sources: README.md, apps/web/README.md
Core Dependencies
The project utilizes several key packages from the TanStack ecosystem to enable data fetching and state management. These are primarily located in the apps/web and apps/api packages.
| Package | Location | Description |
|---|---|---|
@tanstack/react-query | apps/web | The core React library for TanStack Query. |
@tanstack/react-query-devtools | apps/web | Provides development tools for inspecting query states. |
@tanstack/react-router-with-query | apps/web | An adapter to integrate TanStack Query seamlessly with TanStack Router. |
@orpc/react-query | apps/api | A client-side adapter for using TanStack Query with the project's ORPC (type-safe RPC) implementation. |
Sources: apps/web/package.json:20-22,25, apps/api/package.json:17
Setup and Integration
TanStack Query is integrated at the root of the apps/web application to make it available throughout the component tree. The setup involves creating a QueryClient and providing it to the application, typically alongside the router provider.
The root layout component, __root.tsx, is responsible for this integration. It defines a router context that includes the queryClient and renders a TanstackQueryLayout component, which likely contains the QueryClientProvider and the devtools.
Sources: apps/web/README.md, apps/web/src/routes/__root.tsx:13,17,33
Component Integration Flow
The following diagram illustrates how TanStack Query is integrated into the root component of the web application.
This diagram shows that TanstackQueryLayout is rendered within the root component, making the QueryClient available to all routes rendered via the <Outlet />.
Sources: apps/web/src/routes/__root.tsx:30-36
Root Component Configuration
The root route is configured to accept a queryClient instance through its context, which is then passed down to the rest of the application.
// File: apps/web/src/routes/__root.tsx
interface MyRouterContext {
queryClient: QueryClient;
}
export const Route = createRootRouteWithContext<MyRouterContext>()({
// ...
component: () => (
<RootDocument>
<Header />
<Outlet />
<TanStackRouterDevtools />
<TanstackQueryLayout />
</RootDocument>
),
});
Sources: apps/web/src/routes/__root.tsx:15-18, 30-36
Data Fetching Patterns
The project documentation outlines two primary methods for fetching data: using TanStack Router's loader functionality for route-level data and using the useQuery hook for more granular, component-level data fetching.
Sources: apps/web/README.md
Route Loaders
Data can be pre-fetched for a route before it renders using the loader function provided by TanStack Router. This is useful for ensuring that critical data is available immediately when a user navigates to a new page.
The following example from the documentation demonstrates fetching a list of people when the /people route is loaded.
// Example from apps/web/README.md
const peopleRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/people",
loader: async () => {
const response = await fetch("https://swapi.dev/api/people")
return response.json() as Promise<{
results: {
name: string
}[]
}>
},
// ...
})
Sources: apps/web/README.md
useQuery Hook
For more flexible data fetching within components, the useQuery hook is the recommended approach. It handles caching, refetching, and background updates automatically.
The following sequence diagram shows the typical flow when a component uses the useQuery hook to fetch data from a simple API endpoint like the one defined in api.demo-names.ts.
Example API Endpoint
The apps/web application can define its own server-side API routes. The file api.demo-names.ts provides a simple endpoint that returns a static list of names, which can be consumed by a useQuery hook.
// File: apps/web/src/routes/api.demo-names.ts
import { createAPIFileRoute } from "@tanstack/react-start/api";
export const APIRoute = createAPIFileRoute("/api/demo-names")({
GET: async ({ request }) => {
return new Response(JSON.stringify(["Alice", "Bob", "Charlie"]), {
headers: {
"Content-Type": "application/json",
},
});
},
});
Sources: apps/web/src/routes/api.demo-names.ts:3-12
High-Level Architecture
The data fetching architecture connects the React frontend with the backend API through a type-safe contract. TanStack Query acts as the client-side cache and state management layer that communicates with API endpoints, which are defined either within the web app itself or in the dedicated api service using ORPC.
The diagram below shows the high-level relationship between the different parts of the monorepo involved in data fetching.
Sources: README.md, apps/web/package.json, apps/api/package.json
Summary
TanStack Query is a foundational piece of the contract-based-monorepo's frontend architecture. It is set up globally in the apps/web application and provides robust mechanisms for server state management. By integrating with TanStack Router and leveraging a shared API contract with the backend, it enables developers to build efficient, type-safe, and maintainable features that rely on asynchronous data.