Context
In #3855, initializeQueryClient() is called as a top-level side effect in routes.tsx (discussion). This was done because routes.tsx is the top-most file common to both SSR and client entry points. However, executing setup logic in a routes file is a misplaced responsibility.
Current behavior
initializeQueryClient() is called in waspc/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/routes.tsx (line 53), as an imperative side effect alongside the user's setupFn call:
// routes.tsx
await setupFn()
initializeQueryClient()
The function itself lives in sdk/wasp/client/operations/queryClient.ts and creates a QueryClient instance, resolving a promise that the rest of the framework awaits.
Proposed change
Move the initialization to be a top-level await inside queryClient.ts itself, so the module is self-initializing and routes.tsx doesn't need to orchestrate unrelated setup:
// queryClient.ts — self-initializes on import
const queryClient = new QueryClient(queryClientConfig ?? defaultQueryClientConfig)
This would remove the action-at-a-distance pattern and keep query client concerns co-located. The routes file should only be responsible for defining route objects.
Alternatively, we could export the Promise, initialize it in a Suspense, and then make it available through a Context.
Things to consider
configureQueryClient() (public API) must still be callable before the client is created — the initialization order needs to be preserved.
- Both SSR and client entry points must still get a properly initialized client.
References
Context
In #3855,
initializeQueryClient()is called as a top-level side effect inroutes.tsx(discussion). This was done becauseroutes.tsxis the top-most file common to both SSR and client entry points. However, executing setup logic in a routes file is a misplaced responsibility.Current behavior
initializeQueryClient()is called inwaspc/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/routes.tsx(line 53), as an imperative side effect alongside the user'ssetupFncall:The function itself lives in
sdk/wasp/client/operations/queryClient.tsand creates aQueryClientinstance, resolving a promise that the rest of the framework awaits.Proposed change
Move the initialization to be a top-level
awaitinsidequeryClient.tsitself, so the module is self-initializing androutes.tsxdoesn't need to orchestrate unrelated setup:This would remove the action-at-a-distance pattern and keep query client concerns co-located. The routes file should only be responsible for defining route objects.
Alternatively, we could export the Promise, initialize it in a Suspense, and then make it available through a Context.
Things to consider
configureQueryClient()(public API) must still be callable before the client is created — the initialization order needs to be preserved.References