or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

hook-creation.mdindex.mdmutation-hooks.mdquery-hooks.mdquery-keys.mdquery-utilities.mdreact-server-components.mdserver-side-helpers.mdsubscription-hooks.md

server-side-helpers.mddocs/

0

# Server-Side Helpers

1

2

Functions for server-side rendering, static generation, and React Server Components support. These utilities enable data prefetching and hydration in Next.js and other server-side rendering frameworks.

3

4

## Capabilities

5

6

### createServerSideHelpers

7

8

Creates a set of utilities for server-side data fetching and cache management.

9

10

```typescript { .api }

11

/**

12

* Creates server-side helpers for SSG, SSR, and data prefetching

13

* @param opts - Configuration options with router/client and query client

14

* @returns Object with server-side utility functions and dehydration

15

*/

16

function createServerSideHelpers<TRouter extends AnyRouter>(

17

opts: CreateServerSideHelpersOptions<TRouter>

18

): ServerSideHelpers<TRouter>;

19

20

type CreateServerSideHelpersOptions<TRouter extends AnyRouter> =

21

CreateTRPCReactQueryClientConfig & (

22

| CreateSSGHelpersInternal<TRouter>

23

| CreateSSGHelpersExternal<TRouter>

24

);

25

26

interface CreateSSGHelpersInternal<TRouter extends AnyRouter> {

27

/** tRPC router instance */

28

router: TRouter;

29

/** Router context for procedure calls */

30

ctx: inferRouterContext<TRouter>;

31

/** Data transformer configuration */

32

transformer?: TransformerOptions;

33

}

34

35

interface CreateSSGHelpersExternal<TRouter extends AnyRouter> {

36

/** tRPC client instance */

37

client: TRPCClient<TRouter> | TRPCUntypedClient<TRouter>;

38

}

39

40

interface ServerSideHelpers<TRouter> {

41

/** React Query client instance */

42

queryClient: QueryClient;

43

/** Dehydrate query cache for client-side hydration */

44

dehydrate: (opts?: DehydrateOptions) => DehydratedState;

45

46

// Procedure-specific methods are generated based on router structure

47

// Each query procedure gets: fetch, fetchInfinite, prefetch, prefetchInfinite, queryOptions, infiniteQueryOptions

48

}

49

```

50

51

**Usage Examples:**

52

53

```typescript

54

// With router and context (internal)

55

import { createServerSideHelpers } from "@trpc/react-query/server";

56

import { createContext } from "./context";

57

import { appRouter } from "./router";

58

59

export const ssg = createServerSideHelpers({

60

router: appRouter,

61

ctx: await createContext(),

62

transformer: superjson,

63

});

64

65

// With client (external)

66

export const ssg = createServerSideHelpers({

67

client: trpcClient,

68

queryClient: new QueryClient(),

69

});

70

```

71

72

### Server-Side Data Fetching

73

74

Fetch data on the server before rendering components.

75

76

```typescript { .api }

77

/**

78

* Fetch query data on the server

79

* @param input - Input parameters for the procedure

80

* @param opts - Server-side fetch options

81

* @returns Promise resolving to the fetched data

82

*/

83

procedure.fetch(

84

input: TInput,

85

opts?: TRPCFetchQueryOptions<TOutput, TError>

86

): Promise<TOutput>;

87

88

/**

89

* Fetch infinite query data on the server

90

* @param input - Input parameters for the infinite query

91

* @param opts - Server-side fetch infinite options

92

* @returns Promise resolving to the fetched infinite data

93

*/

94

procedure.fetchInfinite(

95

input: GetInfiniteQueryInput<TInput>,

96

opts: TRPCFetchInfiniteQueryOptions<TInput, TOutput, TError>

97

): Promise<InfiniteData<TOutput>>;

98

```

99

100

**Usage Examples:**

101

102

```typescript

103

// Next.js getServerSideProps

104

export async function getServerSideProps(context: GetServerSidePropsContext) {

105

const ssg = createServerSideHelpers({

106

router: appRouter,

107

ctx: await createContext(context),

108

});

109

110

// Fetch user data on the server

111

const user = await ssg.user.get.fetch({ id: 1 });

112

113

// Fetch paginated posts

114

const posts = await ssg.posts.list.fetchInfinite(

115

{ limit: 10 },

116

{ initialCursor: null }

117

);

118

119

return {

120

props: {

121

user,

122

posts,

123

trpcState: ssg.dehydrate(),

124

},

125

};

126

}

127

128

// Next.js getStaticProps

129

export async function getStaticProps() {

130

const ssg = createServerSideHelpers({

131

router: appRouter,

132

ctx: await createContext(),

133

});

134

135

try {

136

await ssg.posts.list.fetch({ limit: 100 });

137

} catch (error) {

138

console.error("Failed to fetch posts:", error);

139

}

140

141

return {

142

props: {

143

trpcState: ssg.dehydrate(),

144

},

145

revalidate: 60, // Revalidate every minute

146

};

147

}

148

```

149

150

### Server-Side Prefetching

151

152

Prefetch data on the server to populate the query cache.

153

154

```typescript { .api }

155

/**

156

* Prefetch query data on the server

157

* @param input - Input parameters for the procedure

158

* @param opts - Server-side prefetch options

159

* @returns Promise that resolves when prefetch is complete

160

*/

161

procedure.prefetch(

162

input: TInput,

163

opts?: TRPCFetchQueryOptions<TOutput, TError>

164

): Promise<void>;

165

166

/**

167

* Prefetch infinite query data on the server

168

* @param input - Input parameters for the infinite query

169

* @param opts - Server-side prefetch infinite options

170

* @returns Promise that resolves when prefetch is complete

171

*/

172

procedure.prefetchInfinite(

173

input: GetInfiniteQueryInput<TInput>,

174

opts: TRPCFetchInfiniteQueryOptions<TInput, TOutput, TError>

175

): Promise<void>;

176

```

177

178

**Usage Examples:**

179

180

```typescript

181

// Prefetch multiple queries

182

export async function getServerSideProps(context: GetServerSidePropsContext) {

183

const ssg = createServerSideHelpers({

184

router: appRouter,

185

ctx: await createContext(context),

186

});

187

188

// Prefetch multiple related queries

189

await Promise.all([

190

ssg.user.get.prefetch({ id: 1 }),

191

ssg.user.posts.prefetch({ userId: 1 }),

192

ssg.posts.list.prefetchInfinite(

193

{ limit: 10 },

194

{ initialCursor: null }

195

),

196

]);

197

198

return {

199

props: {

200

trpcState: ssg.dehydrate(),

201

},

202

};

203

}

204

205

// Conditional prefetching

206

export async function getStaticProps({ params }: GetStaticPropsContext) {

207

const ssg = createServerSideHelpers({

208

router: appRouter,

209

ctx: await createContext(),

210

});

211

212

const userId = Number(params?.userId);

213

214

if (userId) {

215

// Only prefetch if userId is valid

216

await ssg.user.get.prefetch({ id: userId });

217

}

218

219

return {

220

props: {

221

trpcState: ssg.dehydrate(),

222

userId,

223

},

224

};

225

}

226

```

227

228

### Query Options Generation

229

230

Generate React Query options for server-side use.

231

232

```typescript { .api }

233

/**

234

* Generate query options for React Query

235

* @param input - Input parameters for the procedure

236

* @param opts - Query options configuration

237

* @returns React Query options object

238

*/

239

procedure.queryOptions(

240

input: TInput,

241

opts?: TRPCQueryOptions<TOutput, TError>

242

): DefinedTRPCQueryOptionsOut<TOutput, TOutput, TError>;

243

244

/**

245

* Generate infinite query options for React Query

246

* @param input - Input parameters for the infinite query

247

* @param opts - Infinite query options configuration

248

* @returns React Query infinite options object

249

*/

250

procedure.infiniteQueryOptions(

251

input: GetInfiniteQueryInput<TInput>,

252

opts?: TRPCInfiniteQueryOptions<TInput, TOutput, TError>

253

): DefinedTRPCInfiniteQueryOptionsOut<TInput, TOutput, TError>;

254

```

255

256

**Usage Examples:**

257

258

```typescript

259

// Generate query options for custom usage

260

function CustomServerSideLogic() {

261

const ssg = createServerSideHelpers({

262

router: appRouter,

263

ctx: createContext(),

264

});

265

266

// Generate query options

267

const userQueryOptions = ssg.user.get.queryOptions({ id: 1 });

268

const postsQueryOptions = ssg.posts.list.infiniteQueryOptions(

269

{ limit: 10 },

270

{ initialCursor: null }

271

);

272

273

// Use with React Query directly

274

const queryClient = new QueryClient();

275

276

// Prefetch using generated options

277

await queryClient.prefetchQuery(userQueryOptions);

278

await queryClient.prefetchInfiniteQuery(postsQueryOptions);

279

280

return {

281

userQueryOptions,

282

postsQueryOptions,

283

};

284

}

285

```

286

287

### Dehydration and Hydration

288

289

Serialize server-side cache state for client-side hydration.

290

291

```typescript { .api }

292

/**

293

* Dehydrate query cache for client-side hydration

294

* @param opts - Dehydration options

295

* @returns Serialized state for client hydration

296

*/

297

dehydrate(opts?: DehydrateOptions): DehydratedState;

298

299

interface DehydrateOptions {

300

shouldDehydrateQuery?: (query: Query) => boolean;

301

shouldDehydrateMutation?: (mutation: Mutation) => boolean;

302

serializeData?: (data: unknown) => unknown;

303

}

304

```

305

306

**Usage Examples:**

307

308

```typescript

309

// Basic dehydration

310

export async function getServerSideProps() {

311

const ssg = createServerSideHelpers({

312

router: appRouter,

313

ctx: await createContext(),

314

});

315

316

await ssg.user.list.prefetch();

317

318

return {

319

props: {

320

trpcState: ssg.dehydrate(),

321

},

322

};

323

}

324

325

// Custom dehydration options

326

export async function getServerSideProps() {

327

const ssg = createServerSideHelpers({

328

router: appRouter,

329

ctx: await createContext(),

330

});

331

332

await ssg.user.list.prefetch();

333

334

return {

335

props: {

336

trpcState: ssg.dehydrate({

337

shouldDehydrateQuery: (query) => {

338

// Only dehydrate successful queries

339

return query.state.status === 'success';

340

},

341

}),

342

},

343

};

344

}

345

346

// Client-side hydration

347

function MyApp({ Component, pageProps }: AppProps) {

348

const [queryClient] = useState(() => new QueryClient());

349

const [trpcClient] = useState(() => trpc.createClient({

350

url: "http://localhost:3000/api/trpc",

351

}));

352

353

return (

354

<trpc.Provider client={trpcClient} queryClient={queryClient}>

355

<QueryClientProvider client={queryClient}>

356

<Hydrate state={pageProps.trpcState}>

357

<Component {...pageProps} />

358

</Hydrate>

359

</QueryClientProvider>

360

</trpc.Provider>

361

);

362

}

363

```

364

365

366

## Common Patterns

367

368

### Error Handling in SSR

369

370

```typescript

371

export async function getServerSideProps(context: GetServerSidePropsContext) {

372

const ssg = createServerSideHelpers({

373

router: appRouter,

374

ctx: await createContext(context),

375

});

376

377

try {

378

await ssg.user.get.fetch({ id: 1 });

379

} catch (error) {

380

console.error("Failed to fetch user:", error);

381

382

// Handle specific errors

383

if (error.code === "NOT_FOUND") {

384

return { notFound: true };

385

}

386

387

// Return error props

388

return {

389

props: {

390

error: error.message,

391

trpcState: ssg.dehydrate(),

392

},

393

};

394

}

395

396

return {

397

props: {

398

trpcState: ssg.dehydrate(),

399

},

400

};

401

}

402

```

403

404

### Conditional Server-Side Logic

405

406

```typescript

407

export async function getStaticProps({ params }: GetStaticPropsContext) {

408

const ssg = createServerSideHelpers({

409

router: appRouter,

410

ctx: await createContext(),

411

});

412

413

const userId = params?.userId as string;

414

415

if (userId && !isNaN(Number(userId))) {

416

// Only fetch if userId is valid

417

try {

418

await ssg.user.get.fetch({ id: Number(userId) });

419

await ssg.user.posts.prefetch({ userId: Number(userId) });

420

} catch (error) {

421

console.warn(`Failed to fetch data for user ${userId}:`, error);

422

}

423

}

424

425

return {

426

props: {

427

trpcState: ssg.dehydrate(),

428

userId: userId || null,

429

},

430

};

431

}

432

```

433

434

### Performance Optimization

435

436

```typescript

437

export async function getServerSideProps() {

438

const ssg = createServerSideHelpers({

439

router: appRouter,

440

ctx: await createContext(),

441

});

442

443

// Fetch critical data first

444

const criticalData = await ssg.app.config.fetch();

445

446

// Prefetch non-critical data in parallel

447

await Promise.allSettled([

448

ssg.user.recommendations.prefetch(),

449

ssg.posts.trending.prefetch(),

450

ssg.notifications.recent.prefetch(),

451

]);

452

453

return {

454

props: {

455

criticalData,

456

trpcState: ssg.dehydrate({

457

shouldDehydrateQuery: (query) => {

458

// Only dehydrate successful queries to reduce payload size

459

return query.state.status === 'success';

460

},

461

}),

462

},

463

};

464

}

465

```

466

467

### Custom Context Creation

468

469

```typescript

470

async function createSSRContext(context: GetServerSidePropsContext) {

471

const session = await getSession(context.req);

472

473

return {

474

session,

475

req: context.req,

476

res: context.res,

477

};

478

}

479

480

export async function getServerSideProps(context: GetServerSidePropsContext) {

481

const ssg = createServerSideHelpers({

482

router: appRouter,

483

ctx: await createSSRContext(context),

484

});

485

486

if (session?.user) {

487

await ssg.user.profile.prefetch({ id: session.user.id });

488

}

489

490

return {

491

props: {

492

trpcState: ssg.dehydrate(),

493

session,

494

},

495

};

496

}

497

```