or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-router.mdcache-links.mdindex.mdpages-router.mdserver-actions.mdssr.md

cache-links.mddocs/

0

# Cache Links

1

2

Specialized tRPC links for Next.js caching integration with cache tags and revalidation support.

3

4

## Capabilities

5

6

### Next.js Cache Link

7

8

tRPC link that integrates with Next.js `unstable_cache` for server-side caching with tag-based revalidation.

9

10

```typescript { .api }

11

/**

12

* tRPC link that integrates with Next.js unstable_cache for server-side caching

13

* @param opts - Configuration options for cache behavior and context creation

14

* @returns tRPC link with Next.js caching integration

15

*/

16

function experimental_nextCacheLink<TRouter extends AnyRouter>(

17

opts: NextCacheLinkOptions<TRouter>

18

): TRPCLink<TRouter>;

19

20

interface NextCacheLinkOptions<TRouter extends AnyRouter> extends TransformerOptions<inferClientTypes<TRouter>> {

21

/** tRPC router instance */

22

router: TRouter;

23

/** Function to create context for each request */

24

createContext: () => Promise<inferRouterContext<TRouter>>;

25

/** How many seconds the cache should hold before revalidating (default: false) */

26

revalidate?: number | false;

27

}

28

```

29

30

**Usage Examples:**

31

32

```typescript

33

import { experimental_nextCacheLink } from "@trpc/next/app-dir/links/nextCache";

34

import { appRouter } from "./api/root";

35

36

// Basic cache link configuration

37

const cacheLink = experimental_nextCacheLink({

38

router: appRouter,

39

createContext: async () => ({

40

// Your context creation logic

41

userId: getUserId(),

42

db: getDatabase(),

43

}),

44

revalidate: 300, // Cache for 5 minutes

45

});

46

47

// Use in App Router server

48

const trpc = experimental_createTRPCNextAppDirServer({

49

config() {

50

return {

51

links: [cacheLink],

52

};

53

},

54

});

55

56

// Server component with caching

57

async function ProductList() {

58

// This query result will be cached for 5 minutes

59

const products = await trpc.product.list.query();

60

return (

61

<div>

62

{products.map(product => (

63

<div key={product.id}>{product.name}</div>

64

))}

65

</div>

66

);

67

}

68

```

69

70

### Next.js HTTP Link

71

72

HTTP link with Next.js fetch caching and revalidation support for client-side requests.

73

74

```typescript { .api }

75

/**

76

* HTTP link with Next.js fetch caching and revalidation support

77

* @param opts - Configuration options for HTTP requests and caching

78

* @returns tRPC link with Next.js HTTP caching

79

*/

80

function experimental_nextHttpLink<TRouter extends AnyRouter>(

81

opts: NextLinkSingleOptions<TRouter['_def']['_config']['$types']> | NextLinkBatchOptions<TRouter['_def']['_config']['$types']>

82

): TRPCLink<TRouter>;

83

84

interface NextLinkBaseOptions {

85

/** How many seconds the cache should hold before revalidating (default: false) */

86

revalidate?: number | false;

87

/** Whether to batch multiple requests (default: false) */

88

batch?: boolean;

89

}

90

91

type NextLinkSingleOptions<TRoot extends AnyRootTypes> = NextLinkBaseOptions &

92

Omit<HTTPLinkOptions<TRoot>, 'fetch'> & {

93

batch?: false;

94

};

95

96

type NextLinkBatchOptions<TRoot extends AnyRootTypes> = NextLinkBaseOptions &

97

Omit<HTTPBatchLinkOptions<TRoot>, 'fetch'> & {

98

batch: true;

99

};

100

```

101

102

**Usage Examples:**

103

104

```typescript

105

import { experimental_nextHttpLink } from "@trpc/next/app-dir/links/nextHttp";

106

107

// Single request configuration

108

const httpLink = experimental_nextHttpLink({

109

url: "/api/trpc",

110

revalidate: 60, // Cache for 1 minute

111

batch: false,

112

});

113

114

// Batched requests configuration

115

const batchedHttpLink = experimental_nextHttpLink({

116

url: "/api/trpc",

117

revalidate: 120, // Cache for 2 minutes

118

batch: true,

119

maxBatchSize: 10,

120

});

121

122

// Use in client configuration

123

const trpc = experimental_createTRPCNextAppDirClient({

124

config() {

125

return {

126

links: [batchedHttpLink],

127

};

128

},

129

});

130

```

131

132

### Cache Tag Management

133

134

Both cache links use cache tags for fine-grained cache invalidation.

135

136

```typescript { .api }

137

/**

138

* Generates cache tags for procedures based on path and input

139

* @param procedurePath - tRPC procedure path (e.g., "user.getProfile")

140

* @param input - Input parameters for the procedure

141

* @returns Cache tag string for Next.js caching

142

*/

143

function generateCacheTag(procedurePath: string, input: any): string;

144

```

145

146

**Usage Examples:**

147

148

```typescript

149

// Cache tags are automatically generated

150

// For query: trpc.user.getProfile.query({ userId: "123" })

151

// Generated tag: "user.getProfile?input={\"userId\":\"123\"}"

152

153

// For query without input: trpc.posts.list.query()

154

// Generated tag: "posts.list"

155

156

// Manual revalidation using cache tags

157

import { revalidateTag } from "next/cache";

158

159

async function invalidateUserCache(userId: string) {

160

// Revalidate specific user profile

161

revalidateTag(`user.getProfile?input=${JSON.stringify({ userId })}`);

162

163

// Or revalidate all user queries

164

revalidateTag("user.getProfile");

165

}

166

```

167

168

### Per-Request Cache Control

169

170

Override global cache settings on a per-request basis.

171

172

```typescript

173

// Server component with custom cache control

174

async function DynamicContent() {

175

// Override global revalidate setting

176

const data = await trpc.content.get.query(

177

{ slug: "homepage" },

178

{

179

context: {

180

revalidate: 30 // Cache for 30 seconds instead of default

181

}

182

}

183

);

184

185

return <div>{data.content}</div>;

186

}

187

188

// Disable caching for specific request

189

async function RealTimeData() {

190

const data = await trpc.analytics.current.query(

191

{},

192

{

193

context: {

194

revalidate: false // Disable caching

195

}

196

}

197

);

198

199

return <div>Live count: {data.count}</div>;

200

}

201

```

202

203

## Advanced Cache Patterns

204

205

### Hierarchical Cache Invalidation

206

207

```typescript

208

// Organize cache tags hierarchically

209

const userCacheLink = experimental_nextCacheLink({

210

router: appRouter,

211

createContext: async () => ({}),

212

revalidate: 600,

213

});

214

215

// When a user is updated, invalidate all related caches

216

async function updateUser(userId: string, updates: UserUpdate) {

217

// Update user in database

218

await db.user.update(userId, updates);

219

220

// Invalidate all user-related caches

221

revalidateTag(`user.getProfile?input=${JSON.stringify({ userId })}`);

222

revalidateTag(`user.getPosts?input=${JSON.stringify({ userId })}`);

223

revalidateTag(`user.getSettings?input=${JSON.stringify({ userId })}`);

224

225

// Or use a broader invalidation pattern

226

revalidateTag("user.*");

227

}

228

```

229

230

### Cache Warming

231

232

```typescript

233

// Pre-warm cache for common queries

234

async function warmCache() {

235

const commonQueries = [

236

trpc.posts.popular.query(),

237

trpc.categories.list.query(),

238

trpc.settings.public.query(),

239

];

240

241

// Execute queries to populate cache

242

await Promise.all(commonQueries);

243

}

244

245

// Use in page or API route

246

export async function GET() {

247

await warmCache();

248

return new Response("Cache warmed", { status: 200 });

249

}

250

```

251

252

### Conditional Caching

253

254

```typescript

255

const conditionalCacheLink = experimental_nextCacheLink({

256

router: appRouter,

257

createContext: async () => {

258

const user = await getCurrentUser();

259

return { user };

260

},

261

revalidate: (context) => {

262

// Cache authenticated requests longer

263

return context.user ? 300 : 60;

264

},

265

});

266

```

267

268

### Background Revalidation

269

270

```typescript

271

// Set up background revalidation

272

async function setupBackgroundRevalidation() {

273

setInterval(async () => {

274

// Revalidate frequently changing data

275

revalidateTag("analytics.current");

276

revalidateTag("notifications.unread");

277

}, 30000); // Every 30 seconds

278

}

279

280

// Webhook-triggered revalidation

281

export async function POST(request: Request) {

282

const { type, entityId } = await request.json();

283

284

switch (type) {

285

case "user.updated":

286

revalidateTag(`user.getProfile?input=${JSON.stringify({ userId: entityId })}`);

287

break;

288

case "post.published":

289

revalidateTag("posts.list");

290

revalidateTag("posts.popular");

291

break;

292

}

293

294

return new Response("OK");

295

}

296

```

297

298

## Error Handling

299

300

### Cache Link Error Recovery

301

302

```typescript

303

const robustCacheLink = experimental_nextCacheLink({

304

router: appRouter,

305

createContext: async () => {

306

try {

307

return await createContext();

308

} catch (error) {

309

console.error("Context creation failed:", error);

310

// Return minimal context on error

311

return {};

312

}

313

},

314

revalidate: 300,

315

});

316

317

// Handle cache failures gracefully

318

async function CachedComponent() {

319

try {

320

const data = await trpc.data.get.query();

321

return <div>{data.content}</div>;

322

} catch (error) {

323

console.error("Cache query failed:", error);

324

// Fallback to uncached query or default content

325

return <div>Content temporarily unavailable</div>;

326

}

327

}

328

```

329

330

### Cache Monitoring

331

332

```typescript

333

// Monitor cache performance

334

const monitoredCacheLink = experimental_nextCacheLink({

335

router: appRouter,

336

createContext: async () => {

337

const start = Date.now();

338

const context = await createContext();

339

const duration = Date.now() - start;

340

341

// Log slow context creation

342

if (duration > 100) {

343

console.warn(`Slow context creation: ${duration}ms`);

344

}

345

346

return context;

347

},

348

revalidate: 300,

349

});

350

```

351

352

## Types

353

354

```typescript { .api }

355

// Cache link configuration for Next.js unstable_cache

356

interface NextCacheLinkOptions<TRouter extends AnyRouter> extends TransformerOptions<inferClientTypes<TRouter>> {

357

router: TRouter;

358

createContext: () => Promise<inferRouterContext<TRouter>>;

359

revalidate?: number | false;

360

}

361

362

// HTTP link options for Next.js fetch caching

363

interface NextLinkBaseOptions {

364

revalidate?: number | false;

365

batch?: boolean;

366

}

367

368

// tRPC link type

369

interface TRPCLink<TRouter extends AnyRouter> {

370

(runtime: ClientRuntimeConfig): (ctx: OperationContext<TRouter>) => Observable<OperationResult<any>>;

371

}

372

373

// Router context inference

374

type inferRouterContext<TRouter extends AnyRouter> = TRouter['_def']['_config']['$types']['ctx'];

375

376

// Client types inference

377

type inferClientTypes<TRouter extends AnyRouter> = TRouter['_def']['_config']['$types'];

378

```