Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

Server calls and rendering

Hydrate only data produced for the same backend, identity, session, and contract.

Fetch backend data during server rendering, then hydrate the matching frontend client.

Select the server entry point

createClient from @relkit/client works in a server runtime with fetch. For Bun servers, createServerClient from @relkit/client/server adds operation-time trace propagation, and createRelkitServerContext from @relkit/client/server/tanstack-query prepares identity-bound query hydration. The two /server leaves are Bun-only package exports; a Node-based Next.js server should use the base client.

Create clients and caches per incoming request when forwarding user credentials. A browser cookie jar is not inherited by server-side fetch, and one user’s headers must not be shared with another. createServerClient adds tracing, while createRelkitServerContext also loads identity. With the base client or tracing-only client, load identity and supply its headers before a mutation, forwarding the incoming credentials to both requests.

Create request scope

In a Bun server, prefetch the same GET /hello selector and input used by the React example. The query key includes the server-derived identity scope so it matches the managed hook during hydration:

This example assumes the incoming request already carries the backend’s session or anonymous identity cookie. For a first visit without that cookie, let the browser provider establish identity before loading private data. The server context does not forward new Set-Cookie headers to your framework’s response; custom server-side identity bootstrapping must handle that separately.

/** Prefetches Hello for one Bun-rendered request using the same identity-scoped key as the React hook. */
import { createRelkitServerContext } from "@relkit/client/server/tanstack-query";
import { relkitKey } from "@relkit/client/react";
import { QueryClient } from "@tanstack/query-core";

/** Prepares a private hydration snapshot without sharing credentials or cache across requests.
 * @param request - Current incoming request whose credentials may be forwarded.
 * @param baseUrl - The same public backend origin configured on the browser provider.
 * @returns Identity-bound state for RelkitClientProvider's hydratedState prop. */
export async function prefetchGreeting(request: Request, baseUrl: string) {
  const queryClient = new QueryClient();
  try {
    const server = await createRelkitServerContext({ baseUrl, request, queryClient });
    const input = { name: "RELKIT" };
    const scope = server.dehydrate().scope;
    await queryClient.prefetchQuery({
      queryKey: relkitKey(scope, "query", "GET /hello", input),
      queryFn: () => server.client["route.get.hello"](input),
      staleTime: 30_000,
    });
    return server.dehydrate();
  } finally {
    queryClient.clear();
  }
}

Pass the returned state to RelkitClientProvider through its hydratedState prop, using the same baseUrl and any identityKey partition on both sides. Give the browser’s Hello query a matching staleTime: 30_000 when you want to reuse this prefetched value for 30 seconds. Hydration installs only when backend, opaque identity scope, session epoch, public fingerprint, operation, and canonical key match. A fresh hydrated query avoids unintended duplicate requests within its configured staleTime; stale-query revalidation remains intentional.

The generated client supplies the typed query function; relkitKey adds the managed hook’s cache identity. Using an unrelated query key can make a successful prefetch invisible to the browser hook. Keep framework serialization and response-cookie handling in the server layer.

Reference and source