or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdisomorphic-functions.mdmiddleware.mdrequest-response.mdrpc-system.mdserver-functions.mdserver-utilities.mdssr-components.mdvite-plugin.md

rpc-system.mddocs/

0

# RPC System

1

2

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.

3

4

## Capabilities

5

6

### Create Client RPC

7

8

Creates client-side RPC functions that can communicate with server-side endpoints.

9

10

```typescript { .api }

11

/**

12

* Creates a client-side RPC function for server communication

13

* @param functionId - Unique identifier for the RPC function

14

* @param fetcher - Function that handles the actual HTTP request

15

* @returns ClientRpc instance for making server calls

16

*/

17

function createClientRpc(

18

functionId: string,

19

fetcher: (...args: any[]) => Promise<any>

20

): ClientRpc;

21

22

interface ClientRpc {

23

(...args: any[]): Promise<any>;

24

functionId: string;

25

url: string;

26

}

27

```

28

29

**Usage Examples:**

30

31

```typescript

32

import { createClientRpc } from "@tanstack/react-start/client-rpc";

33

34

// Basic RPC function

35

const getUserRpc = createClientRpc(

36

"getUser",

37

async (id: string) => {

38

const response = await fetch(`/api/functions/getUser`, {

39

method: "POST",

40

headers: { "Content-Type": "application/json" },

41

body: JSON.stringify({ id })

42

});

43

return response.json();

44

}

45

);

46

47

// Use in React component

48

function UserProfile({ userId }: { userId: string }) {

49

const [user, setUser] = useState(null);

50

51

useEffect(() => {

52

getUserRpc(userId).then(setUser);

53

}, [userId]);

54

55

return user ? <div>{user.name}</div> : <div>Loading...</div>;

56

}

57

58

// Advanced RPC with error handling

59

const createUserRpc = createClientRpc(

60

"createUser",

61

async (userData: { name: string; email: string }) => {

62

try {

63

const response = await fetch("/api/functions/createUser", {

64

method: "POST",

65

headers: { "Content-Type": "application/json" },

66

body: JSON.stringify(userData)

67

});

68

69

if (!response.ok) {

70

throw new Error(`HTTP ${response.status}: ${response.statusText}`);

71

}

72

73

return response.json();

74

} catch (error) {

75

console.error("RPC call failed:", error);

76

throw error;

77

}

78

}

79

);

80

```

81

82

### Create Server RPC

83

84

Creates server-side RPC functions that can be called from client code.

85

86

```typescript { .api }

87

/**

88

* Creates a server-side RPC function with split import functionality

89

* @param functionId - Unique identifier for the RPC function

90

* @param splitImportFn - Function that handles dynamic imports and execution

91

* @returns ServerRpc instance with URL and function metadata

92

*/

93

function createServerRpc(

94

functionId: string,

95

splitImportFn: (...args: any[]) => any

96

): ServerRpc;

97

98

interface ServerRpc {

99

(...args: any[]): any;

100

url: string;

101

functionId: string;

102

[TSS_SERVER_FUNCTION]: true;

103

}

104

```

105

106

**Usage Examples:**

107

108

```typescript

109

import { createServerRpc } from "@tanstack/react-start/server-rpc";

110

111

// Basic server RPC

112

const getUserServerRpc = createServerRpc(

113

"getUser",

114

async (id: string) => {

115

// Dynamic import for server-only code

116

const { db } = await import("./database");

117

return db.user.findUnique({ where: { id } });

118

}

119

);

120

121

// RPC with complex business logic

122

const processOrderServerRpc = createServerRpc(

123

"processOrder",

124

async (orderData: {

125

items: Array<{ id: string; quantity: number }>;

126

customerId: string;

127

paymentMethod: string;

128

}) => {

129

// Split imports for server-only dependencies

130

const [

131

{ db },

132

{ paymentProcessor },

133

{ inventoryManager },

134

{ emailService }

135

] = await Promise.all([

136

import("./database"),

137

import("./payment"),

138

import("./inventory"),

139

import("./email")

140

]);

141

142

// Process order with full server-side logic

143

const order = await db.order.create({

144

data: {

145

customerId: orderData.customerId,

146

items: {

147

create: orderData.items.map(item => ({

148

productId: item.id,

149

quantity: item.quantity

150

}))

151

}

152

}

153

});

154

155

// Process payment

156

const payment = await paymentProcessor.charge({

157

amount: order.total,

158

method: orderData.paymentMethod,

159

customerId: orderData.customerId

160

});

161

162

// Update inventory

163

await inventoryManager.decrementStock(orderData.items);

164

165

// Send confirmation email

166

await emailService.sendOrderConfirmation(orderData.customerId, order);

167

168

return { order, payment };

169

}

170

);

171

```

172

173

### Server Function Fetcher

174

175

Utilities for handling server function requests and responses.

176

177

```typescript { .api }

178

/**

179

* Server function fetcher for handling RPC requests

180

* @param url - The URL to fetch from

181

* @param options - Fetch options including method and body

182

* @returns Promise resolving to the response

183

*/

184

interface ServerFnFetcher {

185

(url: string, options: ServerFnFetchOptions): Promise<Response>;

186

}

187

188

interface ServerFnFetchOptions {

189

method: string;

190

headers?: HeadersInit;

191

body?: BodyInit;

192

}

193

```

194

195

## RPC Configuration and Environment

196

197

### Environment Variables

198

199

The RPC system uses environment variables for configuration:

200

201

```typescript

202

// Server configuration

203

process.env.TSS_APP_BASE = "/"; // Base path for the application

204

process.env.TSS_SERVER_FN_BASE = "api/functions"; // Base path for server functions

205

```

206

207

### URL Generation

208

209

RPC functions automatically generate URLs based on configuration:

210

211

```typescript

212

// Generated URL structure

213

const baseUrl = `${TSS_APP_BASE}/${TSS_SERVER_FN_BASE}/`;

214

const functionUrl = baseUrl + functionId;

215

216

// Example: /api/functions/getUser

217

```

218

219

## Advanced Usage Patterns

220

221

### RPC with Authentication

222

223

```typescript

224

import { createClientRpc } from "@tanstack/react-start/client-rpc";

225

226

// Authenticated RPC calls

227

const createAuthenticatedRpc = (functionId: string) =>

228

createClientRpc(functionId, async (...args) => {

229

const token = localStorage.getItem("auth-token");

230

231

const response = await fetch(`/api/functions/${functionId}`, {

232

method: "POST",

233

headers: {

234

"Content-Type": "application/json",

235

"Authorization": `Bearer ${token}`

236

},

237

body: JSON.stringify(args)

238

});

239

240

if (response.status === 401) {

241

// Handle authentication error

242

window.location.href = "/login";

243

throw new Error("Authentication required");

244

}

245

246

return response.json();

247

});

248

249

const getUserRpc = createAuthenticatedRpc("getUser");

250

const updateUserRpc = createAuthenticatedRpc("updateUser");

251

```

252

253

### RPC with Caching

254

255

```typescript

256

import { createClientRpc } from "@tanstack/react-start/client-rpc";

257

258

// Cached RPC calls

259

const cache = new Map();

260

261

const createCachedRpc = (functionId: string, cacheTTL = 5 * 60 * 1000) =>

262

createClientRpc(functionId, async (...args) => {

263

const cacheKey = `${functionId}:${JSON.stringify(args)}`;

264

const cached = cache.get(cacheKey);

265

266

if (cached && Date.now() - cached.timestamp < cacheTTL) {

267

return cached.data;

268

}

269

270

const response = await fetch(`/api/functions/${functionId}`, {

271

method: "POST",

272

headers: { "Content-Type": "application/json" },

273

body: JSON.stringify(args)

274

});

275

276

const data = await response.json();

277

cache.set(cacheKey, { data, timestamp: Date.now() });

278

279

return data;

280

});

281

282

const getUserRpc = createCachedRpc("getUser", 10 * 60 * 1000); // 10 minute cache

283

```

284

285

### Server RPC with Validation

286

287

```typescript

288

import { createServerRpc } from "@tanstack/react-start/server-rpc";

289

290

const validateUserInput = (data: unknown) => {

291

if (!data || typeof data !== "object") {

292

throw new Error("Invalid input");

293

}

294

295

const { name, email } = data as any;

296

if (!name || typeof name !== "string") {

297

throw new Error("Name is required");

298

}

299

if (!email || !email.includes("@")) {

300

throw new Error("Valid email is required");

301

}

302

303

return { name, email };

304

};

305

306

const createUserServerRpc = createServerRpc(

307

"createUser",

308

async (input: unknown) => {

309

// Validate input

310

const userData = validateUserInput(input);

311

312

// Import server dependencies

313

const { db } = await import("./database");

314

const { hashPassword } = await import("./crypto");

315

const { sendWelcomeEmail } = await import("./email");

316

317

// Create user with validated data

318

const user = await db.user.create({

319

data: {

320

name: userData.name,

321

email: userData.email,

322

passwordHash: await hashPassword("temporary")

323

}

324

});

325

326

// Send welcome email asynchronously

327

sendWelcomeEmail(user.email, user.name).catch(console.error);

328

329

return { user: { id: user.id, name: user.name, email: user.email } };

330

}

331

);

332

```

333

334

### RPC Error Handling

335

336

```typescript

337

import { createClientRpc } from "@tanstack/react-start/client-rpc";

338

339

const createRobustRpc = (functionId: string, maxRetries = 3) =>

340

createClientRpc(functionId, async (...args) => {

341

let lastError;

342

343

for (let attempt = 1; attempt <= maxRetries; attempt++) {

344

try {

345

const response = await fetch(`/api/functions/${functionId}`, {

346

method: "POST",

347

headers: { "Content-Type": "application/json" },

348

body: JSON.stringify(args)

349

});

350

351

if (!response.ok) {

352

if (response.status >= 500 && attempt < maxRetries) {

353

// Retry on server errors

354

await new Promise(resolve =>

355

setTimeout(resolve, Math.pow(2, attempt) * 1000)

356

);

357

continue;

358

}

359

360

const errorData = await response.json().catch(() => ({}));

361

throw new Error(errorData.message || `HTTP ${response.status}`);

362

}

363

364

return response.json();

365

} catch (error) {

366

lastError = error;

367

if (attempt === maxRetries) break;

368

369

// Exponential backoff for retries

370

await new Promise(resolve =>

371

setTimeout(resolve, Math.pow(2, attempt) * 1000)

372

);

373

}

374

}

375

376

throw lastError;

377

});

378

379

const getUserRpc = createRobustRpc("getUser");

380

```

381

382

## Types

383

384

```typescript { .api }

385

// RPC function types

386

interface ClientRpc {

387

(...args: any[]): Promise<any>;

388

functionId: string;

389

url: string;

390

}

391

392

interface ServerRpc {

393

(...args: any[]): any;

394

url: string;

395

functionId: string;

396

[TSS_SERVER_FUNCTION]: true;

397

}

398

399

// Server function marker

400

declare const TSS_SERVER_FUNCTION: unique symbol;

401

402

// Fetch options for server functions

403

interface ServerFnFetchOptions {

404

method: string;

405

headers?: HeadersInit;

406

body?: BodyInit;

407

}

408

409

// Server function fetcher type

410

interface ServerFnFetcher {

411

(url: string, options: ServerFnFetchOptions): Promise<Response>;

412

}

413

414

// RPC stream types

415

interface RscStream {

416

// React Server Component stream handling

417

read(): Promise<any>;

418

pipe(destination: any): any;

419

}

420

421

// Fetcher data types

422

interface FetcherData {

423

url: string;

424

method: string;

425

headers: Headers;

426

body?: any;

427

}

428

429

interface FetcherBaseOptions {

430

method?: string;

431

headers?: HeadersInit;

432

}

433

434

// Compiled fetcher types

435

interface CompiledFetcherFnOptions {

436

method: string;

437

headers?: HeadersInit;

438

body?: BodyInit;

439

}

440

441

interface CompiledFetcherFn {

442

(url: string, options: CompiledFetcherFnOptions): Promise<Response>;

443

}

444

445

interface Fetcher {

446

fn: CompiledFetcherFn;

447

options: CompiledFetcherFnOptions;

448

}

449

450

// Optional and required fetcher types

451

interface OptionalFetcher {

452

fetcher?: Fetcher;

453

}

454

455

interface RequiredFetcher {

456

fetcher: Fetcher;

457

}

458

```