From 87a78d4504c3093b21fec158d77cabf23664303f Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Tue, 30 Dec 2025 20:08:56 +0800
Subject: [PATCH 1/4] doc: v3.1.0 tanstack-query docs update
---
.../client-sdk/tanstack-query/index.md | 438 +++++++++++-------
1 file changed, 280 insertions(+), 158 deletions(-)
diff --git a/docs/service/client-sdk/tanstack-query/index.md b/docs/service/client-sdk/tanstack-query/index.md
index c9600e56..77bc455c 100644
--- a/docs/service/client-sdk/tanstack-query/index.md
+++ b/docs/service/client-sdk/tanstack-query/index.md
@@ -33,12 +33,39 @@ The integration provides the following features
- Automatic query invalidation upon mutation.
- Automatic optimistic updates (opt-in).
-## Installation
+## Framework Compatibility
+
+### React
+
+- `@tanstack/react-query`: v5+
+- `react` v18+
+
+### Vue
+
+- `@tanstack/vue-query`: v5+
+- `vue` v3+
+
+### Svelte
+
+- `@tanstack/svelte-query`: v6+
+- `svelte` v5.25.0+
+
+:::warning
+
+`@tanstack/svelte-query` v6 leverages Svelte 5's [runes](https://svelte.dev/docs/svelte/what-are-runes) reactivity system. ZenStack is not compatible with prior versions that uses Svelte stores.
-:::info
-TanStack Query version 5.0.0 or later is required.
:::
+### Angular
+
+Not supported yet (need to migrate implementation from ZenStack v2).
+
+### Solid
+
+Not supported yet.
+
+## Installation
+
## Context Provider
@@ -55,7 +82,7 @@ You can configure the query hooks by setting up context. The following options a
- logging
- Enable logging for debugging purposes. Defaults to false.
+ Logging configuration. Pass `true` to log with `console.log`. Pass a `(message) => void` function for custom logging.
Example for using the context provider:
@@ -69,24 +96,24 @@ import { QuerySettingsProvider, type FetchFn } from '@zenstackhq/tanstack-query/
// custom fetch function that adds a custom header
const myFetch: FetchFn = (url, options) => {
- options = options ?? {};
- options.headers = {
- ...options.headers,
- 'x-my-custom-header': 'hello world',
- };
- return fetch(url, options);
+ options = options ?? {};
+ options.headers = {
+ ...options.headers,
+ 'x-my-custom-header': 'hello world',
+ };
+ return fetch(url, options);
};
const queryClient = new QueryClient();
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
- return (
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+ );
}
export default MyApp;
@@ -101,17 +128,17 @@ export default MyApp;
import { provideQuerySettingsContext, type FetchFn } from '@zenstackhq/tanstack-query/vue';
const myFetch: FetchFn = (url, options) => {
- options = options ?? {};
- options.headers = {
- ...options.headers,
- 'x-my-custom-header': 'hello world',
- };
- return fetch(url, options);
+ options = options ?? {};
+ options.headers = {
+ ...options.headers,
+ 'x-my-custom-header': 'hello world',
+ };
+ return fetch(url, options);
};
provideQuerySettingsContext({
- endpoint: 'http://localhost:3000/api/model',
- fetch: myFetch
+ endpoint: 'http://localhost:3000/api/model',
+ fetch: myFetch
});
@@ -125,43 +152,48 @@ provideQuerySettingsContext({
```svelte title='+layout.svelte'
-
-
-
+
+
+
```
+The provided configuration can be overridden at query time. See [Configuration Overrides](#configuration-overrides) for details.
+
## Using the Query Hooks
Call the `useClientQueries` hook to get a root object to access CRUD hooks for all models. From there, using the hooks is pretty much the same as using `ZenStackClient` in backend code.
+
+
+
+
```ts
-// replace "/react" with "/vue", "/svelte" as needed
import { useClientQueries } from '@zenstackhq/tanstack-query/react';
import { schema } from '~/zenstack/schema-lite.ts';
@@ -169,20 +201,96 @@ const client = useClientQueries(schema);
// `usersWithPosts` is typed `User & { posts: Post[] }`
const { data: usersWithPosts } = client.user.useFindMany({
- include: { posts: true },
- orderBy: { createdAt: 'desc' },
+ include: { posts: true },
+ orderBy: { createdAt: 'desc' },
});
const createPost = client.post.useCreate();
function onCreate() {
- createPost.mutate({ title: 'Some new post' });
+ createPost.mutate({ title: 'Some new post' });
}
```
+
+
+
+
+:::info
+If you want the queries to be reactive, make sure to pass reactive objects as arguments when calling the hooks. See [TanStack Query documentation](https://tanstack.com/query/latest/docs/framework/vue/reactivity) for details.
+:::
+
+```ts title="app.vue"
+
+```
+
+
+
+
+
+:::info
+Arguments to the query hooks must be wrapped in a function to make the result reactive. Please check this [TanStack Query documentation](https://tanstack.com/query/latest/docs/framework/svelte/migrate-from-v5-to-v6) if you are migrating from `@tanstack/svelte-query` v5 to v6.
+:::
+
+```ts title="App.svelte"
+
+```
+
+
+
+
+
The `useClientQueries` takes the schema as an argument, and it uses it for both type inference and runtime logic (e.g., automatic query invalidation). This may bring security concerns, because the schema object contains sensitive content like access policies. Using it in the frontend code will expose such information.
-To mitigate the risk, you can pass the additional `--lite` option when running `zen generate`. With that flag on, the CLI will generate an additional "lite" schema object in `schema-lite.ts` with all attributes removed. The lite schema contains all information needed by the query hooks.
+To mitigate the risk, you can pass the additional `--lite` option when running `zen generate`. With that flag on, the CLI will generate an additional "lite" schema object in `schema-lite.ts` with all attributes removed. The lite schema contains all information needed by the query hooks. Check the [CLI Reference](../../../reference/cli.md#generate) for details.
+
+## Configuration Overrides
+
+The query configurations (as described in the [Context Provider](#context-provider) section) can be overridden in a hierarchical manner.
+
+When calling `useClientQueries`, you can pass the `endpoint`, `fetch`, etc. options to override the global configuration for all hooks returned from the call.
+
+```ts
+const client = useClientQueries(schema, { endpoint: '/custom-endpoint' });
+```
+
+Similarly, when calling an individual query or mutation hook, you can also pass the same options to override the configuration for that specific call.
+
+```ts
+const { data } = client.user.useFindMany(
+ { where: { active: true } },
+ { endpoint: '/another-endpoint' }
+);
+```
## Optimistic Update
@@ -198,7 +306,7 @@ Here's an example:
const { mutate: create } = useCreatePost({ optimisticUpdate: true });
function onCreatePost() {
- create({ ... })
+ create({ ... })
}
```
@@ -222,8 +330,8 @@ By default, all queries opt into automatic optimistic update. You can opt-out on
```ts
const { data } = client.post.useFindMany(
- { where: { published: true } },
- { optimisticUpdate: false }
+ { where: { published: true } },
+ { optimisticUpdate: false }
);
```
@@ -244,49 +352,52 @@ import { useClientQueries } from '@zenstackhq/tanstack-query/react';
// post list component with infinite loading
const Posts = () => {
- const client = useClientQueries(schema);
+ const client = useClientQueries(schema);
- const PAGE_SIZE = 10;
+ const PAGE_SIZE = 10;
- const fetchArgs = {
- include: { author: true },
- orderBy: { createdAt: 'desc' as const },
- take: PAGE_SIZE,
- };
+ const fetchArgs = {
+ include: { author: true },
+ orderBy: { createdAt: 'desc' as const },
+ take: PAGE_SIZE,
+ };
- const { data, fetchNextPage, hasNextPage } = client.post.useInfiniteFindMany(fetchArgs, {
- getNextPageParam: (lastPage, pages) => {
- if (lastPage.length < PAGE_SIZE) {
- return undefined;
- }
- const fetched = pages.flatMap((item) => item).length;
- return {
- ...fetchArgs,
- skip: fetched,
- };
- },
- });
-
- return (
- <>
-
-
+
```
@@ -353,51 +464,53 @@ Here's a quick example of using infinite query to load a list of posts with infi
```svelte title='/src/components/Posts.svelte'
-
-
- {#if $query.data}
- {#each $query.data.pages as posts, i (i)}
- {#each posts as post (post.id)}
-
{post.title} by {post.author.email}
- {/each}
- {/each}
- {/if}
-
-
- {#if $query.hasNextPage}
-
- {/if}
+
+
+ {#if $query.data}
+ {#each $query.data.pages as posts, i (i)}
+ {#each posts as post (post.id)}
+
{post.title} by {post.author.email}
+ {/each}
+ {/each}
+ {/if}
+
+
+ {#if $query.hasNextPage}
+
+ {/if}
```
@@ -457,15 +570,24 @@ const queryClient = useQueryClient();
const { queryKey } = useFindUniqueUser({ where: { id: '1' } });
function onCancel() {
- queryClient.cancelQueries({ queryKey, exact: true });
+ queryClient.cancelQueries({ queryKey, exact: true });
}
```
When a cancellation occurs, the query state is reset and the ongoing `fetch` call to the CRUD API is aborted.
-## Example
+## Samples
+
+### Interactive Demo
The following live demo shows how to use the query hooks in a React SPA.
+### Full-Stack Samples
+
+#### Next.js
+
+#### Nuxt
+
+#### SvelteKit
From d7c5a155f9398df98def477fe3b5cc7371ac9366 Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Tue, 30 Dec 2025 20:10:34 +0800
Subject: [PATCH 2/4] update
---
docs/service/client-sdk/tanstack-query/index.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/docs/service/client-sdk/tanstack-query/index.md b/docs/service/client-sdk/tanstack-query/index.md
index 77bc455c..2ed3ff68 100644
--- a/docs/service/client-sdk/tanstack-query/index.md
+++ b/docs/service/client-sdk/tanstack-query/index.md
@@ -588,6 +588,12 @@ The following live demo shows how to use the query hooks in a React SPA.
#### Next.js
+[https://github.com/zenstackhq/zenstack-v3/tree/main/samples/next.js](https://github.com/zenstackhq/zenstack-v3/tree/main/samples/next.js)
+
#### Nuxt
+[https://github.com/zenstackhq/zenstack-v3/tree/main/samples/nuxt](https://github.com/zenstackhq/zenstack-v3/tree/main/samples/nuxt)
+
#### SvelteKit
+
+[https://github.com/zenstackhq/zenstack-v3/tree/main/samples/sveltekit](https://github.com/zenstackhq/zenstack-v3/tree/main/samples/sveltekit)
From 1b7fce94d84ead8a624c7c41034fb0916f15f73c Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Tue, 30 Dec 2025 20:19:03 +0800
Subject: [PATCH 3/4] update
---
docs/service/client-sdk/tanstack-query/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/service/client-sdk/tanstack-query/index.md b/docs/service/client-sdk/tanstack-query/index.md
index 2ed3ff68..9ec36ddd 100644
--- a/docs/service/client-sdk/tanstack-query/index.md
+++ b/docs/service/client-sdk/tanstack-query/index.md
@@ -52,7 +52,7 @@ The integration provides the following features
:::warning
-`@tanstack/svelte-query` v6 leverages Svelte 5's [runes](https://svelte.dev/docs/svelte/what-are-runes) reactivity system. ZenStack is not compatible with prior versions that uses Svelte stores.
+`@tanstack/svelte-query` v6 leverages Svelte 5's [runes](https://svelte.dev/docs/svelte/what-are-runes) reactivity system. ZenStack is not compatible with prior versions that use Svelte stores.
:::
From 7cc55395612f853f8f384952a785a96e5efad465 Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Tue, 30 Dec 2025 20:21:55 +0800
Subject: [PATCH 4/4] update
---
docs/service/client-sdk/tanstack-query/index.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/service/client-sdk/tanstack-query/index.md b/docs/service/client-sdk/tanstack-query/index.md
index 9ec36ddd..f9d1dbf4 100644
--- a/docs/service/client-sdk/tanstack-query/index.md
+++ b/docs/service/client-sdk/tanstack-query/index.md
@@ -497,8 +497,8 @@ Here's a quick example of using infinite query to load a list of posts with infi
- {#if $query.data}
- {#each $query.data.pages as posts, i (i)}
+ {#if query.data}
+ {#each query.data.pages as posts, i (i)}
{#each posts as post (post.id)}
{post.title} by {post.author.email}
{/each}
@@ -506,8 +506,8 @@ Here's a quick example of using infinite query to load a list of posts with infi
{/if}