@srpc.org/react-query 0.20.3
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.
- package/README.md +386 -0
- package/dist/context.d.ts +376 -0
- package/dist/context.js +344 -0
- package/dist/index.d.ts +205 -0
- package/dist/index.js +108 -0
- package/package.json +53 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { SRPC, AnySRPC } from '@srpc/core/server';
|
|
2
|
+
import { AnyRoutes, AnyProcedure, ClientProcedure, DecoratedProcedureRecord } from '@srpc/core/shared';
|
|
3
|
+
import React, { PropsWithChildren } from 'react';
|
|
4
|
+
import { UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module
|
|
8
|
+
*
|
|
9
|
+
* React Query integration for SRPC - Type-safe RPC with automatic query and mutation options.
|
|
10
|
+
*
|
|
11
|
+
* This module provides the core functionality for integrating SRPC with TanStack React Query.
|
|
12
|
+
* It transforms SRPC client procedures into React Query-compatible options objects that can
|
|
13
|
+
* be used with `useQuery` and `useMutation` hooks.
|
|
14
|
+
*
|
|
15
|
+
* @example Basic usage
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { createSRPCClient } from "@srpc/core/client";
|
|
18
|
+
* import { createSRPCQueryOptions } from "@srpc.org/react-query";
|
|
19
|
+
* import { useQuery } from "@tanstack/react-query";
|
|
20
|
+
* import type { AppRouter } from "./server";
|
|
21
|
+
*
|
|
22
|
+
* // Create client
|
|
23
|
+
* const client = createSRPCClient<AppRouter>({ endpoint: "/api" });
|
|
24
|
+
*
|
|
25
|
+
* // Transform to React Query options
|
|
26
|
+
* const srpc = createSRPCQueryOptions({ client });
|
|
27
|
+
*
|
|
28
|
+
* // Use in components
|
|
29
|
+
* function MyComponent() {
|
|
30
|
+
* const { data } = useQuery(srpc.users.getUser.queryOptions(1));
|
|
31
|
+
* return <div>{data?.name}</div>;
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @example With React Context
|
|
36
|
+
* ```typescript
|
|
37
|
+
* import { createSRPCContext } from "@srpc.org/react-query";
|
|
38
|
+
*
|
|
39
|
+
* // Create context and hooks
|
|
40
|
+
* const { SRPCProvider, useSRPC } = createSRPCContext<AppRouter>();
|
|
41
|
+
*
|
|
42
|
+
* // Setup provider
|
|
43
|
+
* function App() {
|
|
44
|
+
* return (
|
|
45
|
+
* <QueryClientProvider client={queryClient}>
|
|
46
|
+
* <SRPCProvider client={rpcClient}>
|
|
47
|
+
* <MyComponent />
|
|
48
|
+
* </SRPCProvider>
|
|
49
|
+
* </QueryClientProvider>
|
|
50
|
+
* );
|
|
51
|
+
* }
|
|
52
|
+
*
|
|
53
|
+
* // Use in components
|
|
54
|
+
* function MyComponent() {
|
|
55
|
+
* const srpc = useSRPC();
|
|
56
|
+
* const { data } = useQuery(srpc.users.getUser.queryOptions(1));
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: <Allow any type cast> */
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Type representing the query and mutation options factory for a single procedure.
|
|
64
|
+
*
|
|
65
|
+
* Each procedure in the SRPC router is transformed to have two methods:
|
|
66
|
+
* - `queryOptions(...args)` - Returns React Query options for `useQuery`
|
|
67
|
+
* - `mutationOptions()` - Returns React Query options for `useMutation`
|
|
68
|
+
*
|
|
69
|
+
* @template Procedure - The client procedure type
|
|
70
|
+
* @template TInput - Input parameters type (inferred from procedure)
|
|
71
|
+
* @template TOutput - Output/return type (inferred from procedure)
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* // For a procedure: getUser(id: number) => Promise<User>
|
|
76
|
+
* type GetUserOptions = OptionsFactory<typeof getUser>;
|
|
77
|
+
*
|
|
78
|
+
* // Has methods:
|
|
79
|
+
* const options: GetUserOptions = {
|
|
80
|
+
* queryOptions: (id: number) => UseQueryOptions<Promise<User>>,
|
|
81
|
+
* mutationOptions: () => UseMutationOptions<Promise<User>, Error, [number]>
|
|
82
|
+
* };
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
type OptionsFactory<Procedure extends ClientProcedure<any>, TInput = Parameters<Procedure>, TOutput = ReturnType<Procedure>> = {
|
|
86
|
+
queryOptions: (...args: Parameters<Procedure>) => UseQueryOptions<TOutput>;
|
|
87
|
+
mutationOptions: () => UseMutationOptions<TOutput, Error, TInput>;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Type that recursively transforms an SRPC router into a structure with React Query options.
|
|
91
|
+
*
|
|
92
|
+
* For each procedure in the router, adds `.queryOptions()` and `.mutationOptions()` methods.
|
|
93
|
+
* Handles nested routers recursively, preserving the router structure.
|
|
94
|
+
*
|
|
95
|
+
* @template TRouter - The SRPC router routes type
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* // Server router
|
|
100
|
+
* const appRouter = s.router({
|
|
101
|
+
* getUser: async (_, id: number) => ({ id, name: "John" }),
|
|
102
|
+
* users: s.router({
|
|
103
|
+
* createUser: async (_, data: UserData) => ({ id: 1, ...data })
|
|
104
|
+
* })
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* // Client with query options
|
|
108
|
+
* type QueryRouter = DecoratedQueryProcedureRecord<typeof appRouter["ipc"]>;
|
|
109
|
+
* // {
|
|
110
|
+
* // getUser: {
|
|
111
|
+
* // queryOptions: (id: number) => UseQueryOptions<User>,
|
|
112
|
+
* // mutationOptions: () => UseMutationOptions<User, Error, [number]>
|
|
113
|
+
* // },
|
|
114
|
+
* // users: {
|
|
115
|
+
* // createUser: {
|
|
116
|
+
* // queryOptions: (data: UserData) => UseQueryOptions<UserResult>,
|
|
117
|
+
* // mutationOptions: () => UseMutationOptions<UserResult, Error, [UserData]>
|
|
118
|
+
* // }
|
|
119
|
+
* // }
|
|
120
|
+
* // }
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
type DecoratedQueryProcedureRecord<TRouter extends AnyRoutes> = {
|
|
124
|
+
[TKey in keyof TRouter]: TRouter[TKey] extends AnyProcedure ? OptionsFactory<ClientProcedure<TRouter[TKey]>> : TRouter[TKey] extends SRPC<any> ? DecoratedQueryProcedureRecord<TRouter[TKey]["ipc"]> : never;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @module
|
|
129
|
+
*
|
|
130
|
+
* React Context integration for SRPC with React Query.
|
|
131
|
+
*
|
|
132
|
+
* This module provides React Context-based access to SRPC procedures with React Query
|
|
133
|
+
* integration. It creates a Provider component and hooks for accessing RPC functionality
|
|
134
|
+
* throughout your React application.
|
|
135
|
+
*
|
|
136
|
+
* @example Complete setup
|
|
137
|
+
* ```typescript
|
|
138
|
+
* import { createSRPCClient } from "@srpc/core/client";
|
|
139
|
+
* import { createSRPCContext } from "@srpc/react-query/context";
|
|
140
|
+
* import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
141
|
+
* import type { AppRouter } from "./server";
|
|
142
|
+
*
|
|
143
|
+
* // Create client
|
|
144
|
+
* const client = createSRPCClient<AppRouter>({ endpoint: "/api" });
|
|
145
|
+
*
|
|
146
|
+
* // Create context and hooks
|
|
147
|
+
* export const { SRPCProvider, useSRPC, useSRPCClient } =
|
|
148
|
+
* createSRPCContext<AppRouter>();
|
|
149
|
+
*
|
|
150
|
+
* // Setup in app
|
|
151
|
+
* function App() {
|
|
152
|
+
* const [queryClient] = useState(() => new QueryClient());
|
|
153
|
+
*
|
|
154
|
+
* return (
|
|
155
|
+
* <QueryClientProvider client={queryClient}>
|
|
156
|
+
* <SRPCProvider client={client}>
|
|
157
|
+
* <YourApp />
|
|
158
|
+
* </SRPCProvider>
|
|
159
|
+
* </QueryClientProvider>
|
|
160
|
+
* );
|
|
161
|
+
* }
|
|
162
|
+
*
|
|
163
|
+
* // Use in components
|
|
164
|
+
* function UserProfile() {
|
|
165
|
+
* const srpc = useSRPC();
|
|
166
|
+
* const { data } = useQuery(srpc.users.getUser.queryOptions(1));
|
|
167
|
+
* return <div>{data?.name}</div>;
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Type definition for the SRPC context value.
|
|
174
|
+
*
|
|
175
|
+
* Contains both the raw client for direct RPC calls and the decorated
|
|
176
|
+
* SRPC object with React Query options.
|
|
177
|
+
*
|
|
178
|
+
* @template TRouter - The SRPC router type
|
|
179
|
+
*
|
|
180
|
+
* @property client - Raw SRPC client for direct procedure calls
|
|
181
|
+
* @property srpc - Decorated procedures with `.queryOptions()` and `.mutationOptions()`
|
|
182
|
+
*/
|
|
183
|
+
interface SRPCContextValue<TRouter extends AnySRPC> {
|
|
184
|
+
client: DecoratedProcedureRecord<TRouter["ipc"]>;
|
|
185
|
+
srpc: DecoratedQueryProcedureRecord<TRouter["ipc"]>;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Return type of `createSRPCContext` containing all React components and hooks.
|
|
189
|
+
*
|
|
190
|
+
* @template TRouter - The SRPC router type
|
|
191
|
+
*
|
|
192
|
+
* @property SRPCContext - React Context object (rarely used directly)
|
|
193
|
+
* @property SRPCProvider - Provider component to wrap your app
|
|
194
|
+
* @property useSRPC - Hook to access decorated procedures with React Query options
|
|
195
|
+
* @property useSRPCClient - Hook to access the raw SRPC client
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* const { SRPCProvider, useSRPC, useSRPCClient } = createSRPCContext<AppRouter>();
|
|
200
|
+
*
|
|
201
|
+
* // SRPCProvider: Wrap your app
|
|
202
|
+
* <SRPCProvider client={rpcClient}>
|
|
203
|
+
* <App />
|
|
204
|
+
* </SRPCProvider>
|
|
205
|
+
*
|
|
206
|
+
* // useSRPC: Get React Query options in components
|
|
207
|
+
* const srpc = useSRPC();
|
|
208
|
+
* const query = useQuery(srpc.users.getUser.queryOptions(1));
|
|
209
|
+
*
|
|
210
|
+
* // useSRPCClient: Get raw client for direct calls
|
|
211
|
+
* const client = useSRPCClient();
|
|
212
|
+
* const result = await client.users.getUser(1);
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
type SRPCContextFactory<TRouter extends AnySRPC> = {
|
|
216
|
+
SRPCContext: React.Context<SRPCContextValue<TRouter>>;
|
|
217
|
+
SRPCProvider: ({ children, client, }: PropsWithChildren<{
|
|
218
|
+
client: DecoratedProcedureRecord<TRouter["ipc"]>;
|
|
219
|
+
}>) => React.JSX.Element;
|
|
220
|
+
useSRPC: () => DecoratedQueryProcedureRecord<TRouter["ipc"]>;
|
|
221
|
+
useSRPCClient: () => DecoratedProcedureRecord<TRouter["ipc"]>;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Factory function to create SRPC React Context, Provider, and hooks.
|
|
225
|
+
*
|
|
226
|
+
* Creates a type-safe React Context system for accessing SRPC procedures throughout
|
|
227
|
+
* your React application. The returned hooks provide access to both React Query
|
|
228
|
+
* integrated procedures and the raw RPC client.
|
|
229
|
+
*
|
|
230
|
+
* @template TRouter - The SRPC router type from your server
|
|
231
|
+
*
|
|
232
|
+
* @returns Object containing:
|
|
233
|
+
* - `SRPCContext` - React Context (rarely needed directly)
|
|
234
|
+
* - `SRPCProvider` - Provider component that accepts `client` prop
|
|
235
|
+
* - `useSRPC()` - Hook returning decorated procedures with `.queryOptions()` and `.mutationOptions()`
|
|
236
|
+
* - `useSRPCClient()` - Hook returning the raw SRPC client for direct calls
|
|
237
|
+
*
|
|
238
|
+
* @example Basic setup
|
|
239
|
+
* ```typescript
|
|
240
|
+
* import { createSRPCClient } from "@srpc/core/client";
|
|
241
|
+
* import { createSRPCContext } from "@srpc.org/react-query";
|
|
242
|
+
* import type { AppRouter } from "./server";
|
|
243
|
+
*
|
|
244
|
+
* // Create client
|
|
245
|
+
* const rpcClient = createSRPCClient<AppRouter>({ endpoint: "/api" });
|
|
246
|
+
*
|
|
247
|
+
* // Create context utilities
|
|
248
|
+
* export const { SRPCProvider, useSRPC, useSRPCClient } =
|
|
249
|
+
* createSRPCContext<AppRouter>();
|
|
250
|
+
*
|
|
251
|
+
* // In root component
|
|
252
|
+
* function App() {
|
|
253
|
+
* return (
|
|
254
|
+
* <QueryClientProvider client={queryClient}>
|
|
255
|
+
* <SRPCProvider client={rpcClient}>
|
|
256
|
+
* <YourApp />
|
|
257
|
+
* </SRPCProvider>
|
|
258
|
+
* </QueryClientProvider>
|
|
259
|
+
* );
|
|
260
|
+
* }
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* @example Using in components with queries
|
|
264
|
+
* ```typescript
|
|
265
|
+
* import { useSRPC } from "./rpc";
|
|
266
|
+
* import { useQuery } from "@tanstack/react-query";
|
|
267
|
+
*
|
|
268
|
+
* function UserProfile({ userId }: { userId: number }) {
|
|
269
|
+
* const srpc = useSRPC();
|
|
270
|
+
*
|
|
271
|
+
* const { data, isLoading, error } = useQuery(
|
|
272
|
+
* srpc.users.getUser.queryOptions(userId)
|
|
273
|
+
* );
|
|
274
|
+
*
|
|
275
|
+
* if (isLoading) return <div>Loading...</div>;
|
|
276
|
+
* if (error) return <div>Error: {error.message}</div>;
|
|
277
|
+
*
|
|
278
|
+
* return (
|
|
279
|
+
* <div>
|
|
280
|
+
* <h2>{data.name}</h2>
|
|
281
|
+
* <p>{data.email}</p>
|
|
282
|
+
* </div>
|
|
283
|
+
* );
|
|
284
|
+
* }
|
|
285
|
+
* ```
|
|
286
|
+
*
|
|
287
|
+
* @example Using in components with mutations
|
|
288
|
+
* ```typescript
|
|
289
|
+
* import { useSRPC } from "./rpc";
|
|
290
|
+
* import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
291
|
+
*
|
|
292
|
+
* function CreateUserForm() {
|
|
293
|
+
* const srpc = useSRPC();
|
|
294
|
+
* const queryClient = useQueryClient();
|
|
295
|
+
*
|
|
296
|
+
* const createUser = useMutation({
|
|
297
|
+
* ...srpc.users.createUser.mutationOptions(),
|
|
298
|
+
* onSuccess: () => {
|
|
299
|
+
* queryClient.invalidateQueries({ queryKey: ["users"] });
|
|
300
|
+
* },
|
|
301
|
+
* });
|
|
302
|
+
*
|
|
303
|
+
* return (
|
|
304
|
+
* <form onSubmit={(e) => {
|
|
305
|
+
* e.preventDefault();
|
|
306
|
+
* const formData = new FormData(e.currentTarget);
|
|
307
|
+
* createUser.mutate({
|
|
308
|
+
* name: formData.get("name") as string,
|
|
309
|
+
* email: formData.get("email") as string,
|
|
310
|
+
* });
|
|
311
|
+
* }}>
|
|
312
|
+
* <input name="name" required />
|
|
313
|
+
* <input name="email" type="email" required />
|
|
314
|
+
* <button disabled={createUser.isPending}>Create User</button>
|
|
315
|
+
* </form>
|
|
316
|
+
* );
|
|
317
|
+
* }
|
|
318
|
+
* ```
|
|
319
|
+
*
|
|
320
|
+
* @example Using raw client for direct calls
|
|
321
|
+
* ```typescript
|
|
322
|
+
* import { useSRPCClient } from "./rpc";
|
|
323
|
+
*
|
|
324
|
+
* function DirectCallExample() {
|
|
325
|
+
* const client = useSRPCClient();
|
|
326
|
+
*
|
|
327
|
+
* const handleClick = async () => {
|
|
328
|
+
* // Direct RPC call without React Query
|
|
329
|
+
* const result = await client.sayHello("World");
|
|
330
|
+
* console.log(result); // "Hello World!"
|
|
331
|
+
* };
|
|
332
|
+
*
|
|
333
|
+
* return <button onClick={handleClick}>Call RPC</button>;
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*
|
|
337
|
+
* @example Nested routers
|
|
338
|
+
* ```typescript
|
|
339
|
+
* function NestedRouterExample() {
|
|
340
|
+
* const srpc = useSRPC();
|
|
341
|
+
*
|
|
342
|
+
* // All nested paths are fully typed
|
|
343
|
+
* const userQuery = useQuery(srpc.users.getUser.queryOptions(1));
|
|
344
|
+
* const adminQuery = useQuery(srpc.users.admin.getStats.queryOptions());
|
|
345
|
+
* const postQuery = useQuery(srpc.posts.drafts.list.queryOptions());
|
|
346
|
+
*
|
|
347
|
+
* return <div>...</div>;
|
|
348
|
+
* }
|
|
349
|
+
* ```
|
|
350
|
+
*
|
|
351
|
+
* @example With Next.js App Router
|
|
352
|
+
* ```typescript
|
|
353
|
+
* // app/providers.tsx
|
|
354
|
+
* "use client";
|
|
355
|
+
*
|
|
356
|
+
* import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
357
|
+
* import { rpcClient, SRPCProvider } from "@/lib/rpc";
|
|
358
|
+
* import { useState } from "react";
|
|
359
|
+
*
|
|
360
|
+
* export function Providers({ children }: { children: React.ReactNode }) {
|
|
361
|
+
* const [queryClient] = useState(() => new QueryClient());
|
|
362
|
+
*
|
|
363
|
+
* return (
|
|
364
|
+
* <QueryClientProvider client={queryClient}>
|
|
365
|
+
* <SRPCProvider client={rpcClient}>
|
|
366
|
+
* {children}
|
|
367
|
+
* </SRPCProvider>
|
|
368
|
+
* </QueryClientProvider>
|
|
369
|
+
* );
|
|
370
|
+
* }
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
declare function createSRPCContext<TRouter extends AnySRPC>(): SRPCContextFactory<TRouter>;
|
|
374
|
+
|
|
375
|
+
export { createSRPCContext };
|
|
376
|
+
export type { SRPCContextFactory, SRPCContextValue };
|