Skip to main content

Webapp Composition

A Labkit-style webapp keeps product code in app folders and isolates Labkit wiring in small adapter files.

Adapter Files

A practical browser layout:

src/shared/auth/session.ts
src/shared/auth/auth-api.ts
src/shared/graphql/endpoints.ts
src/shared/realtime/realtime-connection.ts
src/shared/relay/environment.ts
src/shared/theme/theme-store.ts

Those files expose stable app names such as getAccessToken, refreshStoredAuthSession, createRelayEnvironment, and useAuthState. Routes and components import the app adapters, not raw package factories.

Auth Session

import { useSyncExternalStore } from "react";
import {
cookieRefreshTokenTransport,
createAuthSessionHintStorage,
createWebappAuthSession,
} from "@omgjs/labkit-webapp-auth";

const authSession = createWebappAuthSession({
refreshTokenTransport: cookieRefreshTokenTransport,
sessionHintStorage: createAuthSessionHintStorage({
storageKey: "webapp:auth-session-hint",
}),
});

export const getAccessToken = authSession.getAccessToken;
export const getAuthSession = authSession.getAuthSession;
export const subscribeAuthState = authSession.subscribeAuthState;

export function useAuthState() {
return useSyncExternalStore(
authSession.subscribeAuthState,
authSession.getAuthState,
authSession.getAuthState,
);
}

Relay Environment

import { DefaultWebappRelayRuntime } from "@omgjs/labkit-webapp-graphql-relay";

export const relayRuntime = new DefaultWebappRelayRuntime({
httpEndpoint: HTTP_ENDPOINT,
wsEndpoint: WS_ENDPOINT,
auth,
});

export function createRelayEnvironment() {
return relayRuntime.getEnvironment();
}

The auth adapter provides access-token reads, auth-state subscription, auth-session reads, refresh, credentials, and auth-required error checks. The runtime provides the Relay environment, the realtime instance, and observable connection state.

App Providers

At the top of the React tree, create one Relay environment, bootstrap auth once, and apply theme classes from your app-owned theme adapter.

export function AppProviders() {
const environment = useMemo(() => createRelayEnvironment(), []);

useEffect(() => {
void bootstrapAuthSession();
}, []);

return (
<RelayEnvironmentProvider environment={environment}>
<RouterProvider router={router} />
</RelayEnvironmentProvider>
);
}

Labkit owns reusable runtime and resource-lifetime mechanics. The app owns routes, generated operations, product hooks, UI, and visual design.

Route-Owned Relay Queries

When a TanStack loader creates a Relay query reference that a route component mounts, use createRouteQueryLifetime to bridge loader ownership and mounted React ownership. Return the lifetime with the references and call useRouteQueryLifetime in the route component. Use one lifetime for all references created by one loader and call terminal abort(error) if multi-query construction fails partway.

The application still chooses pending/error components, freshness and cache settings, history behavior, retry UI, and generated operations. The validated policy replaces retired loader data before render with defaultGcTime: 0, defaultStaleTime: 0, and blocking stale reloads. See the 3.1 Relay upgrade guide for complete loader/component examples and teardown rules.