0
# App Router Support
1
2
Experimental features for Next.js App Router including server components, server actions, and advanced caching.
3
4
## Capabilities
5
6
### App Router Client
7
8
Client-side utilities for Next.js App Router with built-in caching and query management.
9
10
```typescript { .api }
11
/**
12
* Creates tRPC client specifically for Next.js App Router with caching support
13
* @param opts - Configuration options for App Router client
14
* @returns tRPC client instance with App Router optimizations
15
*/
16
function experimental_createTRPCNextAppDirClient<TRouter extends AnyRouter>(
17
opts: CreateTRPCNextAppRouterOptions<TRouter>
18
): TRPCClient<TRouter>;
19
20
interface CreateTRPCNextAppRouterOptions<TRouter extends AnyRouter> {
21
/** Function that returns tRPC client configuration */
22
config: () => CreateTRPCClientOptions<TRouter>;
23
}
24
```
25
26
**Usage Examples:**
27
28
```typescript
29
import { experimental_createTRPCNextAppDirClient } from "@trpc/next/app-dir/client";
30
import { httpBatchLink } from "@trpc/client";
31
import type { AppRouter } from "./api/trpc";
32
33
// Create App Router client
34
const trpc = experimental_createTRPCNextAppDirClient<AppRouter>({
35
config() {
36
return {
37
links: [
38
httpBatchLink({
39
url: "/api/trpc",
40
}),
41
],
42
};
43
},
44
});
45
46
// Use in client components
47
async function UserProfile() {
48
const user = await trpc.user.getProfile.query({ userId: "123" });
49
return <div>Hello, {user.name}!</div>;
50
}
51
```
52
53
### App Router Server
54
55
Server-side utilities for Next.js App Router with revalidation and caching support.
56
57
```typescript { .api }
58
/**
59
* Creates server-side tRPC utilities for Next.js App Router
60
* @param opts - Configuration options for App Router server
61
* @returns Decorated router with server-side methods including revalidation
62
*/
63
function experimental_createTRPCNextAppDirServer<TRouter extends AnyRouter>(
64
opts: CreateTRPCNextAppRouterOptions<TRouter>
65
): NextAppDirDecorateRouterRecord<TRouter['_def']['_config']['$types'], TRouter['_def']['record']>;
66
67
type NextAppDirDecorateRouterRecord<TRoot extends AnyRootTypes, TRecord extends RouterRecord> = {
68
[TKey in keyof TRecord]: TRecord[TKey] extends infer $Value
69
? $Value extends AnyProcedure
70
? DecorateProcedureServer<$Value['_def']['type'], {
71
input: inferProcedureInput<$Value>;
72
output: inferTransformedProcedureOutput<TRoot, $Value>;
73
errorShape: TRoot['errorShape'];
74
transformer: TRoot['transformer'];
75
}>
76
: $Value extends RouterRecord
77
? NextAppDirDecorateRouterRecord<TRoot, $Value>
78
: never
79
: never;
80
};
81
```
82
83
**Usage Examples:**
84
85
```typescript
86
import { experimental_createTRPCNextAppDirServer } from "@trpc/next/app-dir/server";
87
import { experimental_nextCacheLink } from "@trpc/next/app-dir/links/nextCache";
88
import { appRouter } from "./api/root";
89
90
// Create server instance
91
const trpc = experimental_createTRPCNextAppDirServer<typeof appRouter>({
92
config() {
93
return {
94
links: [
95
experimental_nextCacheLink({
96
router: appRouter,
97
createContext: async () => ({}),
98
revalidate: 60, // Cache for 60 seconds
99
}),
100
],
101
};
102
},
103
});
104
105
// Use in server components
106
async function UserList() {
107
const users = await trpc.user.list.query();
108
return (
109
<div>
110
{users.map(user => (
111
<div key={user.id}>{user.name}</div>
112
))}
113
</div>
114
);
115
}
116
117
// Revalidate cache
118
async function RefreshUsers() {
119
await trpc.user.list.revalidate();
120
return <p>Users refreshed!</p>;
121
}
122
```
123
124
### Procedure Decoration
125
126
Each procedure in the App Router server gets decorated with additional methods.
127
128
```typescript { .api }
129
type DecorateProcedureServer<TType extends ProcedureType, TDef extends ResolverDef> =
130
TType extends 'query'
131
? {
132
/** Execute the query procedure */
133
query: Resolver<TDef>;
134
/** Revalidate cached results for this query */
135
revalidate: (input?: TDef['input']) => Promise<{ revalidated: false; error: string } | { revalidated: true }>;
136
}
137
: TType extends 'mutation'
138
? {
139
/** Execute the mutation procedure */
140
mutate: Resolver<TDef>;
141
}
142
: TType extends 'subscription'
143
? {
144
/** Execute the subscription procedure */
145
subscribe: Resolver<TDef>;
146
}
147
: never;
148
```
149
150
### Cache Revalidation Endpoint
151
152
HTTP endpoint handler for programmatic cache revalidation.
153
154
```typescript { .api }
155
/**
156
* HTTP endpoint handler for cache tag revalidation
157
* @param req - Request object with JSON body containing cacheTag
158
* @returns Response indicating revalidation success or failure
159
*/
160
async function experimental_revalidateEndpoint(req: Request): Promise<Response>;
161
```
162
163
**Usage Examples:**
164
165
```typescript
166
// In app/api/revalidate/route.ts
167
import { experimental_revalidateEndpoint } from "@trpc/next/app-dir/server";
168
169
export async function POST(req: Request) {
170
return experimental_revalidateEndpoint(req);
171
}
172
173
// Usage from client
174
async function revalidateUserData() {
175
const response = await fetch("/api/revalidate", {
176
method: "POST",
177
headers: { "Content-Type": "application/json" },
178
body: JSON.stringify({ cacheTag: "user.list" }),
179
});
180
181
const result = await response.json();
182
console.log("Revalidated:", result.revalidated);
183
}
184
```
185
186
## Advanced Usage
187
188
### Server Components with Error Boundaries
189
190
```typescript
191
import { Suspense } from "react";
192
import { ErrorBoundary } from "react-error-boundary";
193
194
async function ServerComponentWithTRPC() {
195
try {
196
const data = await trpc.posts.list.query();
197
return (
198
<div>
199
{data.map(post => (
200
<article key={post.id}>
201
<h2>{post.title}</h2>
202
<p>{post.content}</p>
203
</article>
204
))}
205
</div>
206
);
207
} catch (error) {
208
return <div>Failed to load posts</div>;
209
}
210
}
211
212
export default function PostsPage() {
213
return (
214
<ErrorBoundary fallback={<div>Something went wrong</div>}>
215
<Suspense fallback={<div>Loading posts...</div>}>
216
<ServerComponentWithTRPC />
217
</Suspense>
218
</ErrorBoundary>
219
);
220
}
221
```
222
223
### Streaming with Suspense
224
225
```typescript
226
import { Suspense } from "react";
227
228
async function UserProfile({ userId }: { userId: string }) {
229
const user = await trpc.user.getProfile.query({ userId });
230
return <div>Profile: {user.name}</div>;
231
}
232
233
async function UserPosts({ userId }: { userId: string }) {
234
const posts = await trpc.user.getPosts.query({ userId });
235
return (
236
<div>
237
{posts.map(post => (
238
<div key={post.id}>{post.title}</div>
239
))}
240
</div>
241
);
242
}
243
244
export default function UserPage({ params }: { params: { id: string } }) {
245
return (
246
<div>
247
<Suspense fallback={<div>Loading profile...</div>}>
248
<UserProfile userId={params.id} />
249
</Suspense>
250
<Suspense fallback={<div>Loading posts...</div>}>
251
<UserPosts userId={params.id} />
252
</Suspense>
253
</div>
254
);
255
}
256
```
257
258
## Types
259
260
```typescript { .api }
261
// Resolver function type for App Router procedures
262
interface Resolver<TDef extends ResolverDef> {
263
(input: TDef['input']): Promise<TDef['output']>;
264
}
265
266
// Resolver definition for type safety
267
interface ResolverDef {
268
input: any;
269
output: any;
270
transformer: boolean;
271
errorShape: any;
272
}
273
274
// Procedure types supported
275
type ProcedureType = 'query' | 'mutation' | 'subscription';
276
277
// Router record type
278
type RouterRecord = Record<string, AnyProcedure | RouterRecord>;
279
280
// Any procedure type
281
interface AnyProcedure {
282
_def: {
283
type: ProcedureType;
284
};
285
}
286
```