npm-tanstack--react-start

Description
Modern full-stack React framework with SSR, streaming, server functions, and API routes powered by TanStack Router and Vite.
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/npm-tanstack--react-start@1.132.0

rpc-system.md docs/

1
# RPC System
2
3
The RPC (Remote Procedure Call) system provides type-safe client-server communication with automatic serialization and request handling. It enables seamless function calls between client and server environments with full type safety and error handling.
4
5
## Capabilities
6
7
### Create Client RPC
8
9
Creates client-side RPC functions that can communicate with server-side endpoints.
10
11
```typescript { .api }
12
/**
13
* Creates a client-side RPC function for server communication
14
* @param functionId - Unique identifier for the RPC function
15
* @param fetcher - Function that handles the actual HTTP request
16
* @returns ClientRpc instance for making server calls
17
*/
18
function createClientRpc(
19
functionId: string,
20
fetcher: (...args: any[]) => Promise<any>
21
): ClientRpc;
22
23
interface ClientRpc {
24
(...args: any[]): Promise<any>;
25
functionId: string;
26
url: string;
27
}
28
```
29
30
**Usage Examples:**
31
32
```typescript
33
import { createClientRpc } from "@tanstack/react-start/client-rpc";
34
35
// Basic RPC function
36
const getUserRpc = createClientRpc(
37
"getUser",
38
async (id: string) => {
39
const response = await fetch(`/api/functions/getUser`, {
40
method: "POST",
41
headers: { "Content-Type": "application/json" },
42
body: JSON.stringify({ id })
43
});
44
return response.json();
45
}
46
);
47
48
// Use in React component
49
function UserProfile({ userId }: { userId: string }) {
50
const [user, setUser] = useState(null);
51
52
useEffect(() => {
53
getUserRpc(userId).then(setUser);
54
}, [userId]);
55
56
return user ? <div>{user.name}</div> : <div>Loading...</div>;
57
}
58
59
// Advanced RPC with error handling
60
const createUserRpc = createClientRpc(
61
"createUser",
62
async (userData: { name: string; email: string }) => {
63
try {
64
const response = await fetch("/api/functions/createUser", {
65
method: "POST",
66
headers: { "Content-Type": "application/json" },
67
body: JSON.stringify(userData)
68
});
69
70
if (!response.ok) {
71
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
72
}
73
74
return response.json();
75
} catch (error) {
76
console.error("RPC call failed:", error);
77
throw error;
78
}
79
}
80
);
81
```
82
83
### Create Server RPC
84
85
Creates server-side RPC functions that can be called from client code.
86
87
```typescript { .api }
88
/**
89
* Creates a server-side RPC function with split import functionality
90
* @param functionId - Unique identifier for the RPC function
91
* @param splitImportFn - Function that handles dynamic imports and execution
92
* @returns ServerRpc instance with URL and function metadata
93
*/
94
function createServerRpc(
95
functionId: string,
96
splitImportFn: (...args: any[]) => any
97
): ServerRpc;
98
99
interface ServerRpc {
100
(...args: any[]): any;
101
url: string;
102
functionId: string;
103
[TSS_SERVER_FUNCTION]: true;
104
}
105
```
106
107
**Usage Examples:**
108
109
```typescript
110
import { createServerRpc } from "@tanstack/react-start/server-rpc";
111
112
// Basic server RPC
113
const getUserServerRpc = createServerRpc(
114
"getUser",
115
async (id: string) => {
116
// Dynamic import for server-only code
117
const { db } = await import("./database");
118
return db.user.findUnique({ where: { id } });
119
}
120
);
121
122
// RPC with complex business logic
123
const processOrderServerRpc = createServerRpc(
124
"processOrder",
125
async (orderData: {
126
items: Array<{ id: string; quantity: number }>;
127
customerId: string;
128
paymentMethod: string;
129
}) => {
130
// Split imports for server-only dependencies
131
const [
132
{ db },
133
{ paymentProcessor },
134
{ inventoryManager },
135
{ emailService }
136
] = await Promise.all([
137
import("./database"),
138
import("./payment"),
139
import("./inventory"),
140
import("./email")
141
]);
142
143
// Process order with full server-side logic
144
const order = await db.order.create({
145
data: {
146
customerId: orderData.customerId,
147
items: {
148
create: orderData.items.map(item => ({
149
productId: item.id,
150
quantity: item.quantity
151
}))
152
}
153
}
154
});
155
156
// Process payment
157
const payment = await paymentProcessor.charge({
158
amount: order.total,
159
method: orderData.paymentMethod,
160
customerId: orderData.customerId
161
});
162
163
// Update inventory
164
await inventoryManager.decrementStock(orderData.items);
165
166
// Send confirmation email
167
await emailService.sendOrderConfirmation(orderData.customerId, order);
168
169
return { order, payment };
170
}
171
);
172
```
173
174
### Server Function Fetcher
175
176
Utilities for handling server function requests and responses.
177
178
```typescript { .api }
179
/**
180
* Server function fetcher for handling RPC requests
181
* @param url - The URL to fetch from
182
* @param options - Fetch options including method and body
183
* @returns Promise resolving to the response
184
*/
185
interface ServerFnFetcher {
186
(url: string, options: ServerFnFetchOptions): Promise<Response>;
187
}
188
189
interface ServerFnFetchOptions {
190
method: string;
191
headers?: HeadersInit;
192
body?: BodyInit;
193
}
194
```
195
196
## RPC Configuration and Environment
197
198
### Environment Variables
199
200
The RPC system uses environment variables for configuration:
201
202
```typescript
203
// Server configuration
204
process.env.TSS_APP_BASE = "/"; // Base path for the application
205
process.env.TSS_SERVER_FN_BASE = "api/functions"; // Base path for server functions
206
```
207
208
### URL Generation
209
210
RPC functions automatically generate URLs based on configuration:
211
212
```typescript
213
// Generated URL structure
214
const baseUrl = `${TSS_APP_BASE}/${TSS_SERVER_FN_BASE}/`;
215
const functionUrl = baseUrl + functionId;
216
217
// Example: /api/functions/getUser
218
```
219
220
## Advanced Usage Patterns
221
222
### RPC with Authentication
223
224
```typescript
225
import { createClientRpc } from "@tanstack/react-start/client-rpc";
226
227
// Authenticated RPC calls
228
const createAuthenticatedRpc = (functionId: string) =>
229
createClientRpc(functionId, async (...args) => {
230
const token = localStorage.getItem("auth-token");
231
232
const response = await fetch(`/api/functions/${functionId}`, {
233
method: "POST",
234
headers: {
235
"Content-Type": "application/json",
236
"Authorization": `Bearer ${token}`
237
},
238
body: JSON.stringify(args)
239
});
240
241
if (response.status === 401) {
242
// Handle authentication error
243
window.location.href = "/login";
244
throw new Error("Authentication required");
245
}
246
247
return response.json();
248
});
249
250
const getUserRpc = createAuthenticatedRpc("getUser");
251
const updateUserRpc = createAuthenticatedRpc("updateUser");
252
```
253
254
### RPC with Caching
255
256
```typescript
257
import { createClientRpc } from "@tanstack/react-start/client-rpc";
258
259
// Cached RPC calls
260
const cache = new Map();
261
262
const createCachedRpc = (functionId: string, cacheTTL = 5 * 60 * 1000) =>
263
createClientRpc(functionId, async (...args) => {
264
const cacheKey = `${functionId}:${JSON.stringify(args)}`;
265
const cached = cache.get(cacheKey);
266
267
if (cached && Date.now() - cached.timestamp < cacheTTL) {
268
return cached.data;
269
}
270
271
const response = await fetch(`/api/functions/${functionId}`, {
272
method: "POST",
273
headers: { "Content-Type": "application/json" },
274
body: JSON.stringify(args)
275
});
276
277
const data = await response.json();
278
cache.set(cacheKey, { data, timestamp: Date.now() });
279
280
return data;
281
});
282
283
const getUserRpc = createCachedRpc("getUser", 10 * 60 * 1000); // 10 minute cache
284
```
285
286
### Server RPC with Validation
287
288
```typescript
289
import { createServerRpc } from "@tanstack/react-start/server-rpc";
290
291
const validateUserInput = (data: unknown) => {
292
if (!data || typeof data !== "object") {
293
throw new Error("Invalid input");
294
}
295
296
const { name, email } = data as any;
297
if (!name || typeof name !== "string") {
298
throw new Error("Name is required");
299
}
300
if (!email || !email.includes("@")) {
301
throw new Error("Valid email is required");
302
}
303
304
return { name, email };
305
};
306
307
const createUserServerRpc = createServerRpc(
308
"createUser",
309
async (input: unknown) => {
310
// Validate input
311
const userData = validateUserInput(input);
312
313
// Import server dependencies
314
const { db } = await import("./database");
315
const { hashPassword } = await import("./crypto");
316
const { sendWelcomeEmail } = await import("./email");
317
318
// Create user with validated data
319
const user = await db.user.create({
320
data: {
321
name: userData.name,
322
email: userData.email,
323
passwordHash: await hashPassword("temporary")
324
}
325
});
326
327
// Send welcome email asynchronously
328
sendWelcomeEmail(user.email, user.name).catch(console.error);
329
330
return { user: { id: user.id, name: user.name, email: user.email } };
331
}
332
);
333
```
334
335
### RPC Error Handling
336
337
```typescript
338
import { createClientRpc } from "@tanstack/react-start/client-rpc";
339
340
const createRobustRpc = (functionId: string, maxRetries = 3) =>
341
createClientRpc(functionId, async (...args) => {
342
let lastError;
343
344
for (let attempt = 1; attempt <= maxRetries; attempt++) {
345
try {
346
const response = await fetch(`/api/functions/${functionId}`, {
347
method: "POST",
348
headers: { "Content-Type": "application/json" },
349
body: JSON.stringify(args)
350
});
351
352
if (!response.ok) {
353
if (response.status >= 500 && attempt < maxRetries) {
354
// Retry on server errors
355
await new Promise(resolve =>
356
setTimeout(resolve, Math.pow(2, attempt) * 1000)
357
);
358
continue;
359
}
360
361
const errorData = await response.json().catch(() => ({}));
362
throw new Error(errorData.message || `HTTP ${response.status}`);
363
}
364
365
return response.json();
366
} catch (error) {
367
lastError = error;
368
if (attempt === maxRetries) break;
369
370
// Exponential backoff for retries
371
await new Promise(resolve =>
372
setTimeout(resolve, Math.pow(2, attempt) * 1000)
373
);
374
}
375
}
376
377
throw lastError;
378
});
379
380
const getUserRpc = createRobustRpc("getUser");
381
```
382
383
## Types
384
385
```typescript { .api }
386
// RPC function types
387
interface ClientRpc {
388
(...args: any[]): Promise<any>;
389
functionId: string;
390
url: string;
391
}
392
393
interface ServerRpc {
394
(...args: any[]): any;
395
url: string;
396
functionId: string;
397
[TSS_SERVER_FUNCTION]: true;
398
}
399
400
// Server function marker
401
declare const TSS_SERVER_FUNCTION: unique symbol;
402
403
// Fetch options for server functions
404
interface ServerFnFetchOptions {
405
method: string;
406
headers?: HeadersInit;
407
body?: BodyInit;
408
}
409
410
// Server function fetcher type
411
interface ServerFnFetcher {
412
(url: string, options: ServerFnFetchOptions): Promise<Response>;
413
}
414
415
// RPC stream types
416
interface RscStream {
417
// React Server Component stream handling
418
read(): Promise<any>;
419
pipe(destination: any): any;
420
}
421
422
// Fetcher data types
423
interface FetcherData {
424
url: string;
425
method: string;
426
headers: Headers;
427
body?: any;
428
}
429
430
interface FetcherBaseOptions {
431
method?: string;
432
headers?: HeadersInit;
433
}
434
435
// Compiled fetcher types
436
interface CompiledFetcherFnOptions {
437
method: string;
438
headers?: HeadersInit;
439
body?: BodyInit;
440
}
441
442
interface CompiledFetcherFn {
443
(url: string, options: CompiledFetcherFnOptions): Promise<Response>;
444
}
445
446
interface Fetcher {
447
fn: CompiledFetcherFn;
448
options: CompiledFetcherFnOptions;
449
}
450
451
// Optional and required fetcher types
452
interface OptionalFetcher {
453
fetcher?: Fetcher;
454
}
455
456
interface RequiredFetcher {
457
fetcher: Fetcher;
458
}
459
```