Next.js integration for tRPC that enables end-to-end type-safe APIs with enhanced server-side rendering and static site generation capabilities.
—
Experimental features for Next.js App Router including server components, server actions, and advanced caching.
Client-side utilities for Next.js App Router with built-in caching and query management.
/**
* Creates tRPC client specifically for Next.js App Router with caching support
* @param opts - Configuration options for App Router client
* @returns tRPC client instance with App Router optimizations
*/
function experimental_createTRPCNextAppDirClient<TRouter extends AnyRouter>(
opts: CreateTRPCNextAppRouterOptions<TRouter>
): TRPCClient<TRouter>;
interface CreateTRPCNextAppRouterOptions<TRouter extends AnyRouter> {
/** Function that returns tRPC client configuration */
config: () => CreateTRPCClientOptions<TRouter>;
}Usage Examples:
import { experimental_createTRPCNextAppDirClient } from "@trpc/next/app-dir/client";
import { httpBatchLink } from "@trpc/client";
import type { AppRouter } from "./api/trpc";
// Create App Router client
const trpc = experimental_createTRPCNextAppDirClient<AppRouter>({
config() {
return {
links: [
httpBatchLink({
url: "/api/trpc",
}),
],
};
},
});
// Use in client components
async function UserProfile() {
const user = await trpc.user.getProfile.query({ userId: "123" });
return <div>Hello, {user.name}!</div>;
}Server-side utilities for Next.js App Router with revalidation and caching support.
/**
* Creates server-side tRPC utilities for Next.js App Router
* @param opts - Configuration options for App Router server
* @returns Decorated router with server-side methods including revalidation
*/
function experimental_createTRPCNextAppDirServer<TRouter extends AnyRouter>(
opts: CreateTRPCNextAppRouterOptions<TRouter>
): NextAppDirDecorateRouterRecord<TRouter['_def']['_config']['$types'], TRouter['_def']['record']>;
type NextAppDirDecorateRouterRecord<TRoot extends AnyRootTypes, TRecord extends RouterRecord> = {
[TKey in keyof TRecord]: TRecord[TKey] extends infer $Value
? $Value extends AnyProcedure
? DecorateProcedureServer<$Value['_def']['type'], {
input: inferProcedureInput<$Value>;
output: inferTransformedProcedureOutput<TRoot, $Value>;
errorShape: TRoot['errorShape'];
transformer: TRoot['transformer'];
}>
: $Value extends RouterRecord
? NextAppDirDecorateRouterRecord<TRoot, $Value>
: never
: never;
};Usage Examples:
import { experimental_createTRPCNextAppDirServer } from "@trpc/next/app-dir/server";
import { experimental_nextCacheLink } from "@trpc/next/app-dir/links/nextCache";
import { appRouter } from "./api/root";
// Create server instance
const trpc = experimental_createTRPCNextAppDirServer<typeof appRouter>({
config() {
return {
links: [
experimental_nextCacheLink({
router: appRouter,
createContext: async () => ({}),
revalidate: 60, // Cache for 60 seconds
}),
],
};
},
});
// Use in server components
async function UserList() {
const users = await trpc.user.list.query();
return (
<div>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
// Revalidate cache
async function RefreshUsers() {
await trpc.user.list.revalidate();
return <p>Users refreshed!</p>;
}Each procedure in the App Router server gets decorated with additional methods.
type DecorateProcedureServer<TType extends ProcedureType, TDef extends ResolverDef> =
TType extends 'query'
? {
/** Execute the query procedure */
query: Resolver<TDef>;
/** Revalidate cached results for this query */
revalidate: (input?: TDef['input']) => Promise<{ revalidated: false; error: string } | { revalidated: true }>;
}
: TType extends 'mutation'
? {
/** Execute the mutation procedure */
mutate: Resolver<TDef>;
}
: TType extends 'subscription'
? {
/** Execute the subscription procedure */
subscribe: Resolver<TDef>;
}
: never;HTTP endpoint handler for programmatic cache revalidation.
/**
* HTTP endpoint handler for cache tag revalidation
* @param req - Request object with JSON body containing cacheTag
* @returns Response indicating revalidation success or failure
*/
async function experimental_revalidateEndpoint(req: Request): Promise<Response>;Usage Examples:
// In app/api/revalidate/route.ts
import { experimental_revalidateEndpoint } from "@trpc/next/app-dir/server";
export async function POST(req: Request) {
return experimental_revalidateEndpoint(req);
}
// Usage from client
async function revalidateUserData() {
const response = await fetch("/api/revalidate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ cacheTag: "user.list" }),
});
const result = await response.json();
console.log("Revalidated:", result.revalidated);
}import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
async function ServerComponentWithTRPC() {
try {
const data = await trpc.posts.list.query();
return (
<div>
{data.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
))}
</div>
);
} catch (error) {
return <div>Failed to load posts</div>;
}
}
export default function PostsPage() {
return (
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<Suspense fallback={<div>Loading posts...</div>}>
<ServerComponentWithTRPC />
</Suspense>
</ErrorBoundary>
);
}import { Suspense } from "react";
async function UserProfile({ userId }: { userId: string }) {
const user = await trpc.user.getProfile.query({ userId });
return <div>Profile: {user.name}</div>;
}
async function UserPosts({ userId }: { userId: string }) {
const posts = await trpc.user.getPosts.query({ userId });
return (
<div>
{posts.map(post => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
}
export default function UserPage({ params }: { params: { id: string } }) {
return (
<div>
<Suspense fallback={<div>Loading profile...</div>}>
<UserProfile userId={params.id} />
</Suspense>
<Suspense fallback={<div>Loading posts...</div>}>
<UserPosts userId={params.id} />
</Suspense>
</div>
);
}// Resolver function type for App Router procedures
interface Resolver<TDef extends ResolverDef> {
(input: TDef['input']): Promise<TDef['output']>;
}
// Resolver definition for type safety
interface ResolverDef {
input: any;
output: any;
transformer: boolean;
errorShape: any;
}
// Procedure types supported
type ProcedureType = 'query' | 'mutation' | 'subscription';
// Router record type
type RouterRecord = Record<string, AnyProcedure | RouterRecord>;
// Any procedure type
interface AnyProcedure {
_def: {
type: ProcedureType;
};
}Install with Tessl CLI
npx tessl i tessl/npm-trpc--next