@navios/react-query 0.7.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +171 -1
  2. package/README.md +152 -4
  3. package/coverage/base.css +224 -0
  4. package/coverage/block-navigation.js +87 -0
  5. package/coverage/client/declare-client.mts.html +1264 -0
  6. package/coverage/client/index.html +116 -0
  7. package/coverage/clover.xml +160 -0
  8. package/coverage/coverage-final.json +8 -0
  9. package/coverage/favicon.png +0 -0
  10. package/coverage/index.html +146 -0
  11. package/coverage/mutation/index.html +131 -0
  12. package/coverage/mutation/key-creator.mts.html +277 -0
  13. package/coverage/mutation/make-hook.mts.html +952 -0
  14. package/coverage/prettify.css +1 -0
  15. package/coverage/prettify.js +2 -0
  16. package/coverage/query/index.html +161 -0
  17. package/coverage/query/key-creator.mts.html +415 -0
  18. package/coverage/query/make-infinite-options.mts.html +601 -0
  19. package/coverage/query/make-options.mts.html +838 -0
  20. package/coverage/query/prefetch.mts.html +1063 -0
  21. package/coverage/sort-arrow-sprite.png +0 -0
  22. package/coverage/sorter.js +210 -0
  23. package/dist/src/__tests__/errorSchema.spec.d.mts +2 -0
  24. package/dist/src/__tests__/errorSchema.spec.d.mts.map +1 -0
  25. package/dist/src/__tests__/prefetch.spec.d.mts +2 -0
  26. package/dist/src/__tests__/prefetch.spec.d.mts.map +1 -0
  27. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts +2 -0
  28. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts.map +1 -0
  29. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts +2 -0
  30. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts.map +1 -0
  31. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts +2 -0
  32. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts.map +1 -0
  33. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts +2 -0
  34. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts.map +1 -0
  35. package/dist/src/client/__type-tests__/query.spec-d.d.mts +2 -0
  36. package/dist/src/client/__type-tests__/query.spec-d.d.mts.map +1 -0
  37. package/dist/src/client/declare-client.d.mts +15 -8
  38. package/dist/src/client/declare-client.d.mts.map +1 -1
  39. package/dist/src/client/types/from-endpoint.d.mts +130 -0
  40. package/dist/src/client/types/from-endpoint.d.mts.map +1 -0
  41. package/dist/src/client/types/helpers.d.mts +74 -0
  42. package/dist/src/client/types/helpers.d.mts.map +1 -0
  43. package/dist/src/client/types/index.d.mts +21 -0
  44. package/dist/src/client/types/index.d.mts.map +1 -0
  45. package/dist/src/client/types/infinite-query.d.mts +61 -0
  46. package/dist/src/client/types/infinite-query.d.mts.map +1 -0
  47. package/dist/src/client/types/multipart-mutation.d.mts +98 -0
  48. package/dist/src/client/types/multipart-mutation.d.mts.map +1 -0
  49. package/dist/src/client/types/mutation.d.mts +75 -0
  50. package/dist/src/client/types/mutation.d.mts.map +1 -0
  51. package/dist/src/client/types/query.d.mts +65 -0
  52. package/dist/src/client/types/query.d.mts.map +1 -0
  53. package/dist/src/client/types.d.mts +1 -608
  54. package/dist/src/client/types.d.mts.map +1 -1
  55. package/dist/src/common/types.d.mts +40 -3
  56. package/dist/src/common/types.d.mts.map +1 -1
  57. package/dist/src/mutation/index.d.mts +1 -0
  58. package/dist/src/mutation/index.d.mts.map +1 -1
  59. package/dist/src/mutation/make-hook.d.mts +42 -16
  60. package/dist/src/mutation/make-hook.d.mts.map +1 -1
  61. package/dist/src/mutation/optimistic.d.mts +172 -0
  62. package/dist/src/mutation/optimistic.d.mts.map +1 -0
  63. package/dist/src/mutation/types.d.mts +41 -20
  64. package/dist/src/mutation/types.d.mts.map +1 -1
  65. package/dist/src/query/index.d.mts +1 -0
  66. package/dist/src/query/index.d.mts.map +1 -1
  67. package/dist/src/query/key-creator.d.mts.map +1 -1
  68. package/dist/src/query/make-infinite-options.d.mts +3 -2
  69. package/dist/src/query/make-infinite-options.d.mts.map +1 -1
  70. package/dist/src/query/make-options.d.mts +42 -12
  71. package/dist/src/query/make-options.d.mts.map +1 -1
  72. package/dist/src/query/prefetch.d.mts +245 -0
  73. package/dist/src/query/prefetch.d.mts.map +1 -0
  74. package/dist/src/query/types.d.mts +25 -18
  75. package/dist/src/query/types.d.mts.map +1 -1
  76. package/dist/tsconfig.tsbuildinfo +1 -1
  77. package/lib/index.cjs +451 -28
  78. package/lib/index.cjs.map +1 -1
  79. package/lib/index.d.cts +1019 -600
  80. package/lib/index.d.cts.map +1 -1
  81. package/lib/index.d.mts +1016 -597
  82. package/lib/index.d.mts.map +1 -1
  83. package/lib/index.mjs +447 -29
  84. package/lib/index.mjs.map +1 -1
  85. package/package.json +3 -3
  86. package/src/__tests__/declare-client.spec.mts +229 -2
  87. package/src/__tests__/errorSchema.spec.mts +391 -0
  88. package/src/__tests__/make-mutation.spec.mts +6 -5
  89. package/src/__tests__/makeDataTag.spec.mts +2 -1
  90. package/src/__tests__/makeQueryOptions.spec.mts +2 -1
  91. package/src/__tests__/prefetch.spec.mts +310 -0
  92. package/src/client/__type-tests__/from-endpoint.spec-d.mts +550 -0
  93. package/src/client/__type-tests__/infinite-query.spec-d.mts +648 -0
  94. package/src/client/__type-tests__/multipart-mutation.spec-d.mts +725 -0
  95. package/src/client/__type-tests__/mutation.spec-d.mts +757 -0
  96. package/src/client/__type-tests__/query.spec-d.mts +701 -0
  97. package/src/client/declare-client.mts +59 -34
  98. package/src/client/types/from-endpoint.mts +344 -0
  99. package/src/client/types/helpers.mts +140 -0
  100. package/src/client/types/index.mts +26 -0
  101. package/src/client/types/infinite-query.mts +133 -0
  102. package/src/client/types/multipart-mutation.mts +264 -0
  103. package/src/client/types/mutation.mts +176 -0
  104. package/src/client/types/query.mts +132 -0
  105. package/src/client/types.mts +1 -1935
  106. package/src/common/types.mts +67 -3
  107. package/src/mutation/index.mts +1 -0
  108. package/src/mutation/make-hook.mts +171 -63
  109. package/src/mutation/optimistic.mts +300 -0
  110. package/src/mutation/types.mts +87 -30
  111. package/src/query/index.mts +1 -0
  112. package/src/query/key-creator.mts +24 -13
  113. package/src/query/make-infinite-options.mts +53 -10
  114. package/src/query/make-options.mts +184 -43
  115. package/src/query/prefetch.mts +326 -0
  116. package/src/query/types.mts +56 -17
  117. package/src/client/__type-tests__/client-instance.spec-d.mts +0 -852
@@ -1,34 +1,83 @@
1
- import type { AbstractEndpoint, AnyEndpointConfig } from '@navios/builder'
1
+ import type {
2
+ EndpointHandler,
3
+ EndpointOptions,
4
+ InferEndpointReturn,
5
+ Simplify,
6
+ } from '@navios/builder'
2
7
  import type {
3
8
  DataTag,
4
9
  QueryClient,
5
10
  UseQueryOptions,
6
11
  UseSuspenseQueryOptions,
7
12
  } from '@tanstack/react-query'
13
+ import type { ZodObject, ZodType } from 'zod/v4'
8
14
 
9
15
  import { queryOptions, useQuery, useSuspenseQuery } from '@tanstack/react-query'
10
16
 
11
17
  import type { Split } from '../common/types.mjs'
12
- import type { QueryArgs, QueryParams } from './types.mjs'
18
+ import type { QueryArgs, QueryHelpers, QueryResult } from './types.mjs'
13
19
 
14
20
  import { createQueryKey } from './key-creator.mjs'
15
21
 
22
+ /**
23
+ * Options for makeQueryOptions.
24
+ */
25
+ export interface MakeQueryOptionsParams<
26
+ Options extends EndpointOptions,
27
+ UseDiscriminator extends boolean = false,
28
+ Result = QueryResult<Options, UseDiscriminator>,
29
+ > {
30
+ keyPrefix?: string[]
31
+ keySuffix?: string[]
32
+ onFail?: (err: unknown) => void
33
+ processResponse: (
34
+ data: InferEndpointReturn<Options, UseDiscriminator>,
35
+ ) => Result
36
+ }
37
+
16
38
  /**
17
39
  * Creates query options for a given endpoint.
18
40
  *
19
41
  * Returns a function that generates TanStack Query options when called with params.
20
42
  * The returned function also has helper methods attached (use, useSuspense, invalidate, etc.)
21
43
  *
22
- * @param endpoint - The navios endpoint to create query options for
44
+ * Uses const generics pattern to automatically infer types from the endpoint configuration.
45
+ *
46
+ * @param endpoint - The navios endpoint handler (from builder's declareEndpoint)
23
47
  * @param options - Query configuration including processResponse
24
48
  * @param baseQuery - Optional base query options to merge
25
49
  * @returns A function that generates query options with attached helpers
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const getUser = api.declareEndpoint({
54
+ * method: 'GET',
55
+ * url: '/users/$userId',
56
+ * responseSchema: userSchema,
57
+ * })
58
+ *
59
+ * const queryOptions = makeQueryOptions(getUser, {
60
+ * processResponse: (data) => data,
61
+ * })
62
+ *
63
+ * const { data } = queryOptions.useSuspense({ urlParams: { userId: '123' } })
64
+ * ```
26
65
  */
27
66
  export function makeQueryOptions<
28
- Config extends AnyEndpointConfig,
29
- Options extends QueryParams<Config>,
67
+ const Options extends EndpointOptions,
68
+ UseDiscriminator extends boolean = false,
69
+ Result = QueryResult<Options, UseDiscriminator>,
30
70
  BaseQuery extends Omit<
31
- UseQueryOptions<ReturnType<Options['processResponse']>, Error, any>,
71
+ UseQueryOptions<Result, Error, any>,
72
+ | 'queryKey'
73
+ | 'queryFn'
74
+ | 'getNextPageParam'
75
+ | 'initialPageParam'
76
+ | 'enabled'
77
+ | 'throwOnError'
78
+ | 'placeholderData'
79
+ > = Omit<
80
+ UseQueryOptions<Result, Error, any>,
32
81
  | 'queryKey'
33
82
  | 'queryFn'
34
83
  | 'getNextPageParam'
@@ -38,34 +87,58 @@ export function makeQueryOptions<
38
87
  | 'placeholderData'
39
88
  >,
40
89
  >(
41
- endpoint: AbstractEndpoint<Config>,
42
- options: Options,
43
- baseQuery: BaseQuery = {} as BaseQuery,
44
- ) {
90
+ endpoint: EndpointHandler<Options, UseDiscriminator>,
91
+ options: MakeQueryOptionsParams<Options, UseDiscriminator, Result>,
92
+ baseQuery?: BaseQuery,
93
+ ): ((
94
+ params: Simplify<
95
+ QueryArgs<
96
+ Options['url'],
97
+ Options extends { querySchema: infer Q extends ZodObject }
98
+ ? Q
99
+ : undefined,
100
+ Options extends { requestSchema: infer R extends ZodType } ? R : undefined
101
+ >
102
+ >,
103
+ ) => UseSuspenseQueryOptions<
104
+ Result,
105
+ Error,
106
+ BaseQuery extends { select: (...args: any[]) => infer T } ? T : Result,
107
+ DataTag<Split<Options['url'], '/'>, Result, Error>
108
+ >) &
109
+ QueryHelpers<
110
+ Options['url'],
111
+ Options extends { querySchema: infer Q extends ZodObject } ? Q : undefined,
112
+ Result,
113
+ false,
114
+ Options extends { requestSchema: infer R extends ZodType } ? R : undefined
115
+ > {
45
116
  const config = endpoint.config
46
- const queryKey = createQueryKey(config, options, false)
117
+ const queryKey = createQueryKey(config as any, options as any, false)
47
118
  const processResponse = options.processResponse
48
119
 
49
120
  const result = (
50
- params: QueryArgs<Config['url'], Config['querySchema']>,
51
- ): Options['processResponse'] extends (...args: any[]) => infer Result
52
- ? UseSuspenseQueryOptions<
53
- Result,
54
- Error,
55
- BaseQuery['select'] extends (...args: any[]) => infer T ? T : Result,
56
- DataTag<Split<Config['url'], '/'>, Result, Error>
121
+ params: Simplify<
122
+ QueryArgs<
123
+ Options['url'],
124
+ Options extends { querySchema: infer Q extends ZodObject }
125
+ ? Q
126
+ : undefined,
127
+ Options extends { requestSchema: infer R extends ZodType }
128
+ ? R
129
+ : undefined
57
130
  >
58
- : never => {
59
- // @ts-expect-error TS2322 We know that the processResponse is defined
131
+ >,
132
+ ): any => {
60
133
  return queryOptions({
61
- queryKey: queryKey.dataTag(params),
62
- queryFn: async ({ signal }): Promise<ReturnType<Options['processResponse']>> => {
134
+ queryKey: queryKey.dataTag(params as any),
135
+ queryFn: async ({ signal }): Promise<Result> => {
63
136
  let result
64
137
  try {
65
138
  result = await endpoint({
66
139
  signal,
67
140
  ...params,
68
- })
141
+ } as any)
69
142
  } catch (err) {
70
143
  if (options.onFail) {
71
144
  options.onFail(err)
@@ -73,38 +146,106 @@ export function makeQueryOptions<
73
146
  throw err
74
147
  }
75
148
 
76
- return processResponse(result) as ReturnType<Options['processResponse']>
149
+ return processResponse(result)
77
150
  },
78
151
  ...baseQuery,
79
152
  })
80
153
  }
81
- result.queryKey = queryKey
82
- result.use = (params: QueryArgs<Config['url'], Config['querySchema']>) => {
154
+
155
+ /** The query key creator for this endpoint */
156
+ result.queryKey = queryKey as any
157
+
158
+ /**
159
+ * React hook that executes the query.
160
+ * Uses `useQuery` from TanStack Query internally.
161
+ *
162
+ * @param params - URL parameters, query parameters, and request body
163
+ * @returns Query result with data, isLoading, error, etc.
164
+ */
165
+ result.use = (params: any) => {
83
166
  return useQuery(result(params))
84
167
  }
85
168
 
86
- result.useSuspense = (params: QueryArgs<Config['url'], Config['querySchema']>) => {
169
+ /**
170
+ * React hook that executes the query with Suspense support.
171
+ * Uses `useSuspenseQuery` from TanStack Query internally.
172
+ * The component will suspend while loading and throw on error.
173
+ *
174
+ * @param params - URL parameters, query parameters, and request body
175
+ * @returns Query result with data guaranteed to be defined
176
+ */
177
+ result.useSuspense = (params: any) => {
87
178
  return useSuspenseQuery(result(params))
88
179
  }
89
180
 
90
- result.invalidate = (
91
- queryClient: QueryClient,
92
- params: QueryArgs<Config['url'], Config['querySchema']>,
93
- ) => {
94
- return queryClient.invalidateQueries({
95
- queryKey: result.queryKey.dataTag(params),
96
- })
181
+ /**
182
+ * Creates a function that invalidates a specific query in the cache.
183
+ * Call the returned function to trigger the invalidation.
184
+ *
185
+ * @param queryClient - The TanStack Query client instance
186
+ * @param params - The exact parameters used for this query
187
+ * @returns A function that when called invalidates the query
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * const invalidate = getUser.invalidate(queryClient, { urlParams: { userId: '123' } })
192
+ * await invalidate() // Invalidates this specific query
193
+ * ```
194
+ */
195
+ result.invalidate = (queryClient: QueryClient, params: any) => {
196
+ return () =>
197
+ queryClient.invalidateQueries({
198
+ queryKey: result.queryKey.dataTag(params),
199
+ })
97
200
  }
98
201
 
99
- result.invalidateAll = (
100
- queryClient: QueryClient,
101
- params: QueryArgs<Config['url'], Config['querySchema']>,
102
- ) => {
103
- return queryClient.invalidateQueries({
104
- queryKey: result.queryKey.filterKey(params),
105
- exact: false,
106
- })
202
+ /**
203
+ * Creates a function that invalidates all queries matching the URL pattern.
204
+ * Useful for invalidating all queries for a resource regardless of query params.
205
+ *
206
+ * @param queryClient - The TanStack Query client instance
207
+ * @param params - URL parameters only (query params are ignored for matching)
208
+ * @returns A function that when called invalidates all matching queries
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * const invalidateAll = getUserPosts.invalidateAll(queryClient, { urlParams: { userId: '123' } })
213
+ * await invalidateAll() // Invalidates all getUserPosts queries for user 123
214
+ * ```
215
+ */
216
+ result.invalidateAll = (queryClient: QueryClient, params: any) => {
217
+ return () =>
218
+ queryClient.invalidateQueries({
219
+ queryKey: result.queryKey.filterKey(params),
220
+ exact: false,
221
+ })
107
222
  }
108
223
 
109
- return result
224
+ return result as unknown as ((
225
+ params: Simplify<
226
+ QueryArgs<
227
+ Options['url'],
228
+ Options extends { querySchema: infer Q extends ZodObject }
229
+ ? Q
230
+ : undefined,
231
+ Options extends { requestSchema: infer R extends ZodType }
232
+ ? R
233
+ : undefined
234
+ >
235
+ >,
236
+ ) => UseSuspenseQueryOptions<
237
+ Result,
238
+ Error,
239
+ BaseQuery extends { select: (...args: any[]) => infer T } ? T : Result,
240
+ DataTag<Split<Options['url'], '/'>, Result, Error>
241
+ >) &
242
+ QueryHelpers<
243
+ Options['url'],
244
+ Options extends { querySchema: infer Q extends ZodObject }
245
+ ? Q
246
+ : undefined,
247
+ Result,
248
+ false,
249
+ Options extends { requestSchema: infer R extends ZodType } ? R : undefined
250
+ >
110
251
  }
@@ -0,0 +1,326 @@
1
+ import type {
2
+ FetchQueryOptions,
3
+ QueryClient,
4
+ QueryKey,
5
+ } from '@tanstack/react-query'
6
+
7
+ /**
8
+ * Type for a query options creator function that returns TanStack Query options.
9
+ *
10
+ * This matches the return type of `client.query()` from the navios react-query client.
11
+ */
12
+ export type QueryOptionsCreator<TParams, TData, TError = Error> = (
13
+ params: TParams,
14
+ ) => FetchQueryOptions<TData, TError, TData, QueryKey>
15
+
16
+ /**
17
+ * Helper utilities for prefetching queries.
18
+ *
19
+ * @template TParams - The query parameters type
20
+ * @template TData - The query data type
21
+ * @template TError - The error type (defaults to Error)
22
+ */
23
+ export interface PrefetchHelper<TParams, TData, TError = Error> {
24
+ /**
25
+ * Prefetch query data on the server.
26
+ *
27
+ * Use this in server components or getServerSideProps to
28
+ * prefetch data before rendering.
29
+ *
30
+ * @param queryClient - The QueryClient instance
31
+ * @param params - Parameters for the query
32
+ * @returns Promise that resolves when prefetch is complete
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * // In a Next.js Server Component
37
+ * const queryClient = new QueryClient()
38
+ * await prefetch.prefetch(queryClient, { urlParams: { userId: '1' } })
39
+ * return (
40
+ * <HydrationBoundary state={dehydrate(queryClient)}>
41
+ * <UserProfile userId="1" />
42
+ * </HydrationBoundary>
43
+ * )
44
+ * ```
45
+ */
46
+ prefetch: (queryClient: QueryClient, params: TParams) => Promise<void>
47
+
48
+ /**
49
+ * Ensure query data exists in cache. Fetches only if not cached.
50
+ *
51
+ * Returns the cached or fetched data.
52
+ *
53
+ * @param queryClient - The QueryClient instance
54
+ * @param params - Parameters for the query
55
+ * @returns Promise that resolves to the query data
56
+ *
57
+ * @example
58
+ * ```tsx
59
+ * // Ensure data exists before rendering
60
+ * const userData = await prefetch.ensureData(queryClient, {
61
+ * urlParams: { userId: '1' },
62
+ * })
63
+ * console.log('User:', userData.name)
64
+ * ```
65
+ */
66
+ ensureData: (queryClient: QueryClient, params: TParams) => Promise<TData>
67
+
68
+ /**
69
+ * Get the query options for a given set of parameters.
70
+ *
71
+ * Useful for advanced use cases or when you need to
72
+ * customize the prefetch behavior.
73
+ *
74
+ * @param params - Parameters for the query
75
+ * @returns The query options object
76
+ *
77
+ * @example
78
+ * ```tsx
79
+ * const options = prefetch.getQueryOptions({ urlParams: { userId: '1' } })
80
+ * await queryClient.prefetchQuery({
81
+ * ...options,
82
+ * staleTime: 60000, // Override stale time for prefetch
83
+ * })
84
+ * ```
85
+ */
86
+ getQueryOptions: (
87
+ params: TParams,
88
+ ) => FetchQueryOptions<TData, TError, TData, QueryKey>
89
+
90
+ /**
91
+ * Prefetch multiple queries in parallel.
92
+ *
93
+ * @param queryClient - The QueryClient instance
94
+ * @param paramsList - Array of parameters for multiple queries
95
+ * @returns Promise that resolves when all prefetches complete
96
+ *
97
+ * @example
98
+ * ```tsx
99
+ * // Prefetch multiple users in parallel
100
+ * await prefetch.prefetchMany(queryClient, [
101
+ * { urlParams: { userId: '1' } },
102
+ * { urlParams: { userId: '2' } },
103
+ * { urlParams: { userId: '3' } },
104
+ * ])
105
+ * ```
106
+ */
107
+ prefetchMany: (
108
+ queryClient: QueryClient,
109
+ paramsList: TParams[],
110
+ ) => Promise<void>
111
+ }
112
+
113
+ /**
114
+ * Creates a type-safe prefetch helper for SSR/RSC.
115
+ *
116
+ * This utility wraps a query options creator to provide convenient
117
+ * methods for server-side data fetching and hydration.
118
+ *
119
+ * @param queryOptionsCreator - A function that creates query options (from client.query())
120
+ * @returns A prefetch helper object with prefetch, ensureData, and getQueryOptions methods
121
+ *
122
+ * @example
123
+ * ```tsx
124
+ * // 1. Create your query
125
+ * const getUserQuery = client.query({
126
+ * method: 'GET',
127
+ * url: '/users/$userId',
128
+ * responseSchema: userSchema,
129
+ * })
130
+ *
131
+ * // 2. Create prefetch helper
132
+ * const userPrefetch = createPrefetchHelper(getUserQuery)
133
+ *
134
+ * // 3. Use in server component
135
+ * async function UserPage({ userId }: { userId: string }) {
136
+ * const queryClient = new QueryClient()
137
+ *
138
+ * await userPrefetch.prefetch(queryClient, {
139
+ * urlParams: { userId },
140
+ * })
141
+ *
142
+ * return (
143
+ * <HydrationBoundary state={dehydrate(queryClient)}>
144
+ * <UserProfile userId={userId} />
145
+ * </HydrationBoundary>
146
+ * )
147
+ * }
148
+ * ```
149
+ *
150
+ * @example
151
+ * ```tsx
152
+ * // With Next.js App Router
153
+ * import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
154
+ * import { createPrefetchHelper } from '@navios/react-query'
155
+ *
156
+ * // Define queries
157
+ * const getPostsQuery = client.query({
158
+ * method: 'GET',
159
+ * url: '/posts',
160
+ * querySchema: z.object({ page: z.number() }),
161
+ * responseSchema: postsSchema,
162
+ * })
163
+ *
164
+ * const postsPrefetch = createPrefetchHelper(getPostsQuery)
165
+ *
166
+ * // Server Component
167
+ * export default async function PostsPage() {
168
+ * const queryClient = new QueryClient()
169
+ *
170
+ * await postsPrefetch.prefetch(queryClient, {
171
+ * params: { page: 1 },
172
+ * })
173
+ *
174
+ * return (
175
+ * <HydrationBoundary state={dehydrate(queryClient)}>
176
+ * <PostsList />
177
+ * </HydrationBoundary>
178
+ * )
179
+ * }
180
+ * ```
181
+ */
182
+ export function createPrefetchHelper<TParams, TData, TError = Error>(
183
+ queryOptionsCreator: QueryOptionsCreator<TParams, TData, TError>,
184
+ ): PrefetchHelper<TParams, TData, TError> {
185
+ return {
186
+ prefetch: async (queryClient: QueryClient, params: TParams) => {
187
+ const options = queryOptionsCreator(params)
188
+ await queryClient.prefetchQuery(options)
189
+ },
190
+
191
+ ensureData: async (queryClient: QueryClient, params: TParams) => {
192
+ const options = queryOptionsCreator(params)
193
+ return queryClient.ensureQueryData(options)
194
+ },
195
+
196
+ getQueryOptions: (params: TParams) => {
197
+ return queryOptionsCreator(params)
198
+ },
199
+
200
+ prefetchMany: async (queryClient: QueryClient, paramsList: TParams[]) => {
201
+ await Promise.all(
202
+ paramsList.map((params) => {
203
+ const options = queryOptionsCreator(params)
204
+ return queryClient.prefetchQuery(options)
205
+ }),
206
+ )
207
+ },
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Creates multiple prefetch helpers from a record of query options creators.
213
+ *
214
+ * Useful when you have multiple queries that need to be prefetched together.
215
+ *
216
+ * @param queries - Record of query options creator functions
217
+ * @returns Record of prefetch helpers with the same keys
218
+ *
219
+ * @example
220
+ * ```tsx
221
+ * // Define all your queries
222
+ * const queries = {
223
+ * user: client.query({
224
+ * method: 'GET',
225
+ * url: '/users/$userId',
226
+ * responseSchema: userSchema,
227
+ * }),
228
+ * posts: client.query({
229
+ * method: 'GET',
230
+ * url: '/users/$userId/posts',
231
+ * responseSchema: postsSchema,
232
+ * }),
233
+ * }
234
+ *
235
+ * // Create all prefetch helpers at once
236
+ * const prefetchers = createPrefetchHelpers(queries)
237
+ *
238
+ * // Use in server component
239
+ * async function UserPage({ userId }: { userId: string }) {
240
+ * const queryClient = new QueryClient()
241
+ *
242
+ * await Promise.all([
243
+ * prefetchers.user.prefetch(queryClient, { urlParams: { userId } }),
244
+ * prefetchers.posts.prefetch(queryClient, { urlParams: { userId } }),
245
+ * ])
246
+ *
247
+ * return (
248
+ * <HydrationBoundary state={dehydrate(queryClient)}>
249
+ * <UserProfileWithPosts userId={userId} />
250
+ * </HydrationBoundary>
251
+ * )
252
+ * }
253
+ * ```
254
+ */
255
+ export function createPrefetchHelpers<
256
+ T extends Record<string, QueryOptionsCreator<any, any, any>>,
257
+ >(
258
+ queries: T,
259
+ ): {
260
+ [K in keyof T]: T[K] extends QueryOptionsCreator<
261
+ infer TParams,
262
+ infer TData,
263
+ infer TError
264
+ >
265
+ ? PrefetchHelper<TParams, TData, TError>
266
+ : never
267
+ } {
268
+ const result = {} as {
269
+ [K in keyof T]: T[K] extends QueryOptionsCreator<
270
+ infer TParams,
271
+ infer TData,
272
+ infer TError
273
+ >
274
+ ? PrefetchHelper<TParams, TData, TError>
275
+ : never
276
+ }
277
+
278
+ for (const key of Object.keys(queries) as Array<keyof T>) {
279
+ // @ts-expect-error - TypeScript can't infer this properly
280
+ result[key] = createPrefetchHelper(queries[key])
281
+ }
282
+
283
+ return result
284
+ }
285
+
286
+ /**
287
+ * Prefetch multiple queries from different query creators in parallel.
288
+ *
289
+ * @param queryClient - The QueryClient instance
290
+ * @param prefetches - Array of { helper, params } objects
291
+ * @returns Promise that resolves when all prefetches complete
292
+ *
293
+ * @example
294
+ * ```tsx
295
+ * const userPrefetch = createPrefetchHelper(getUserQuery)
296
+ * const postsPrefetch = createPrefetchHelper(getPostsQuery)
297
+ *
298
+ * async function DashboardPage({ userId }: { userId: string }) {
299
+ * const queryClient = new QueryClient()
300
+ *
301
+ * await prefetchAll(queryClient, [
302
+ * { helper: userPrefetch, params: { urlParams: { userId } } },
303
+ * { helper: postsPrefetch, params: { urlParams: { userId }, params: { limit: 10 } } },
304
+ * ])
305
+ *
306
+ * return (
307
+ * <HydrationBoundary state={dehydrate(queryClient)}>
308
+ * <Dashboard userId={userId} />
309
+ * </HydrationBoundary>
310
+ * )
311
+ * }
312
+ * ```
313
+ */
314
+ export async function prefetchAll(
315
+ queryClient: QueryClient,
316
+ prefetches: Array<{
317
+ helper: PrefetchHelper<any, any, any>
318
+ params: unknown
319
+ }>,
320
+ ): Promise<void> {
321
+ await Promise.all(
322
+ prefetches.map(({ helper, params }) =>
323
+ helper.prefetch(queryClient, params),
324
+ ),
325
+ )
326
+ }