UNPKG

kitcn

Version:

kitcn - React Query integration and CLI tools for Convex

175 lines (141 loc) 4.6 kB
## 7. Client Core (after auth gate) ### 7.1 Auth client setup Prerequisite: 1. Section 6.10 / Section 11.3 auth sign-in gate is green. **Create:** `src/lib/convex/auth-client.ts` ```ts import type { Auth } from "@convex/auth-shared"; import { adminClient, inferAdditionalFields } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; import { convexClient } from "kitcn/auth/client"; import { createAuthMutations } from "kitcn/react"; export const authClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_SITE_URL!, sessionOptions: { // Disable session polling on tab focus (saves ~500ms HTTP call per focus) refetchOnWindowFocus: false, }, plugins: [inferAdditionalFields<Auth>(), adminClient(), convexClient()], }); export const { useActiveOrganization, useListOrganizations } = authClient; export const { useSignOutMutationOptions, useSignInSocialMutationOptions, useSignInMutationOptions, useSignUpMutationOptions, } = createAuthMutations(authClient); ``` ### 7.2 QueryClient setup **Create:** `src/lib/convex/query-client.ts` ```ts import { type DefaultOptions, defaultShouldDehydrateQuery, QueryCache, QueryClient, } from "@tanstack/react-query"; import { isCRPCClientError, isCRPCError } from "kitcn/crpc"; import SuperJSON from "superjson"; export const hydrationConfig: Pick<DefaultOptions, "dehydrate" | "hydrate"> = { dehydrate: { serializeData: SuperJSON.serialize, shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === "pending", shouldRedactErrors: () => false, }, hydrate: { deserializeData: SuperJSON.deserialize, }, }; export function createQueryClient() { return new QueryClient({ queryCache: new QueryCache({ onError: (error) => { if (isCRPCClientError(error)) { console.warn(`[CRPC] ${error.code}:`, error.functionName); } }, }), defaultOptions: { ...hydrationConfig, queries: { retry: (failureCount, error) => { if (isCRPCError(error)) return false; return failureCount < 3; }, }, }, }); } ``` ### 7.3 cRPC React context **Create:** `src/lib/convex/crpc.tsx` Preconditions: 1. Complete Section 5.5 first so `@convex/api` exists. 2. Complete Section 7.4 so `CRPCProvider` is mounted before any `useCRPC()` call. ```tsx import { api } from "@convex/api"; import { createCRPCContext } from "kitcn/react"; export const { CRPCProvider, useCRPC, useCRPCClient } = createCRPCContext({ api, convexSiteUrl: process.env.NEXT_PUBLIC_CONVEX_SITE_URL!, }); ``` ### 7.4 Provider composition **Create:** `src/lib/convex/convex-provider.tsx` Hard rule: 1. Any component using `useCRPC`, `useCRPCClient`, or cRPC hooks must render under `CRPCProvider`. 2. If not, runtime will crash with `useCRPC must be used within CRPCProvider`. ```tsx "use client"; import { QueryClientProvider as TanstackQueryClientProvider } from "@tanstack/react-query"; import { ConvexAuthProvider } from "kitcn/auth/client"; import { ConvexReactClient, getConvexQueryClientSingleton, getQueryClientSingleton, useAuthStore, } from "kitcn/react"; import type { ReactNode } from "react"; import { authClient } from "./auth-client"; import { CRPCProvider } from "./crpc"; import { createQueryClient } from "./query-client"; const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); export function AppConvexProvider({ children, token, }: { children: ReactNode; token?: string; }) { return ( <ConvexAuthProvider authClient={authClient} client={convex} initialToken={token} > <QueryProvider>{children}</QueryProvider> </ConvexAuthProvider> ); } function QueryProvider({ children }: { children: ReactNode }) { const authStore = useAuthStore(); const queryClient = getQueryClientSingleton(createQueryClient); const convexQueryClient = getConvexQueryClientSingleton({ authStore, convex, queryClient, }); return ( <TanstackQueryClientProvider client={queryClient}> <CRPCProvider convexClient={convex} convexQueryClient={convexQueryClient}> {children} </CRPCProvider> </TanstackQueryClientProvider> ); } ``` Provider mount checklist: 1. `AppConvexProvider` wraps app routes before client feature components render. 2. `CRPCProvider` is nested inside TanStack Query provider (`QueryClientProvider`). 3. Next.js apps pass token where required (Section 8.A.4) or intentionally run without token for public-only paths.