Server
For the complete documentation index, see llms.txt. Prefer markdown by appending .md to documentation URLs or sending Accept: text/markdown.

Data fetching

Page loading, TanStack Query caching, mutations, and UI states, with a clear boundary between customer data in the browser and server operations.

Data fetching connects a page to the information it needs and keeps that information current after a customer changes something. Edge Kit combines TanStack Router, TanStack Query, and server functions so you can build this flow without maintaining a separate client API layer.

The router coordinates navigation. TanStack Query manages fetched data and its state in the interface. Server functions perform the private operation on the Worker.

Why TanStack Query?

TanStack Query handles cached results, loading states, and refetching across screens. Its integration with the router lets navigation and components reuse the same data, while mutations can refresh only the views affected by a change.

Page data

A page can prepare data during navigation through a route loader, or request it from a component when the interaction needs it. The kit connects the router and Query for server rendering and hydration, allowing prepared query data to reach the browser with the page.

  • Page requirements: use a loader for information the destination depends on.
  • Secondary content: load panels or customer-triggered details when they are needed. This keeps the initial page focused while allowing the rest of the interface to become interactive progressively.

Shared query definitions let a route and its components describe the same request. They bring the cache key and fetching behavior together, making the request reusable across views.

Private operations

Loaders and components coordinate the request. Authentication, ownership, and paid access remain in the server operation, even if a parent route already checked the session.

Query keys

A query key identifies one result in the client cache. Include the values that change that result: the resource, active customer, filters, and pagination where relevant.

For example, the conceptual key for a customer's filtered notes list is:

["notes", customerId, { search, page }];

Those values distinguish the cached views. They do not authorize the request. The server still derives the customer from the session and validates the filters.

Keep unrelated data in separate keys. Changing an avatar should not require reloading every product list, while a change that affects several views should refresh the keys those views depend on.

Freshness

Freshness determines when previously fetched data can be reused. A product catalog may tolerate a longer interval than a live job status or a customer's latest changes.

The kit configures Query defaults and lets individual queries choose their own policy. Router preloading integrates with Query so query freshness governs the prepared data. Adjust a query's policy around how frequently that data changes and what the customer expects to see.

A notes list could reuse its fetched result for 30 seconds. In this example, listNotes is your feature's protected server function, which resolves ownership from the session:

import { queryOptions } from "@tanstack/react-query";

const notesQuery = queryOptions({
  queryKey: ["notes", customerId],
  queryFn: () => listNotes(),
  staleTime: 30_000, 
});

Freshness and polling are separate settings. The query becomes eligible for refetching after that period; it does not automatically send a request every 30 seconds.

A browser cache can make repeat visits feel immediate, but it is not durable storage. Reloading, signing out, or using another device may require a new server request. Persist customer work in D1, and use KV caching when the server itself needs reusable values.

Mutations

A mutation changes server state, such as updating a profile or creating a document. Its completed request and the refreshed interface are two related parts of the experience.

After a successful mutation, invalidate or update the affected query. A query-backed list then obtains the new result. If a page also depends on independent loader data, refresh that route data as part of the completion flow.

For the customer-scoped notes keys above, the successful save can invalidate every matching list, including filtered or paginated variants:

await queryClient.invalidateQueries({
  queryKey: ["notes", customerId],
});

Use the application's existing query client. Matching queries become stale, and active queries refetch by default; another customer's keys are outside this prefix.

The feature should own this refresh decision because it knows what changed. A generic button or dialog can report success to its caller without knowing every query used by the product.

For optimistic updates, show the expected result immediately only when you can reconcile or roll back a failed operation. A loading state followed by the confirmed result is a simpler starting point for important account or billing changes.

Interface states

Each data view needs more than a successful result:

StateCustomer experience
Initial loadingA stable placeholder or loading indicator
Background refreshExisting content remains usable where appropriate
Empty resultContext about the empty state and a relevant next action
Failed requestA useful message and a retry or recovery path
Pending mutationClear progress and protection against accidental repeat submission
Completed mutationUpdated content and confirmation of the saved result

The kit includes shared mutation error feedback. Add field-level or feature-specific messages where the customer needs more detail, and keep server diagnostics in application logs.

Sessions and cache boundaries

Customer-specific data belongs to the active session. Include customer identity in new private query keys and clear or refresh relevant state when the customer signs out or changes accounts.

A cached page can remain visible after a session expires. The next protected request must still be checked by the server and the interface should guide the customer back through sign-in when required. Sessions explains the distinction between visible account state and current server access.

Feature integration

Start with the server operation and the result the view needs. Define a query for that result, connect the loading and empty states, then add the mutation and its refresh behavior. Check the experience with an empty account, a second customer, a slow request, and a failed request.

The feature recipe includes the complete query and mutation pattern. Forms covers collecting and validating the input that starts the operation.

How is this guide?

Last updated on

On this page

Ship globally on the edge. In minutes.Try Edge Kit