Stop manually setting up tRPC in Next.js — use this CLI instead
Source: Dev.to
Introduction
Every time I start a new Next.js project with tRPC I have to: open the docs, copy files, install packages, wrap layout.tsx, fix the QueryClient error, and repeat the process for the next project. To eliminate this repetitive setup I created a CLI:
npx create-trpc-setup
What the CLI Does
Running the command inside any existing Next.js project automatically:
- Installs the required packages (
@trpc/server,@trpc/client,@trpc/tanstack-react-query,@tanstack/react-query,zod, etc.). - Detects your package manager (npm, pnpm, yarn, bun).
- Reads
tsconfig.jsonto respect path aliases (@/*,~/, or custom aliases). - Detects authentication providers (Clerk or NextAuth) and configures the context accordingly.
- Adjusts to your folder structure (
src/or root layout).
It then generates the following files:
trpc/
├── init.ts ← context, baseProcedure, protectedProcedure, Zod error formatter
├── query-client.ts ← SSR‑safe QueryClient
├── client.tsx ← TRPCReactProvider + useTRPC hook
├── server.tsx ← prefetch, HydrateClient, caller
└── routers/
└── _app.ts ← health + greet procedures with Zod
app/api/trpc/[trpc]/route.ts ← API handler with real headers
app/trpc-status/ ← test page (delete after confirming)
Updating layout.tsx
Before
{children}
After
{children}
Using the Generated Setup
Server Component – Prefetch Data
// app/page.tsx
import { HydrateClient, prefetch, trpc } from "@/trpc/server";
import { MyClient } from "./my-client";
export default function Page() {
prefetch(trpc.greet.queryOptions({ name: "World" }));
return (
);
}
Client Component – Consume Data
// my-client.tsx
"use client";
import { useSuspenseQuery } from "@tanstack/react-query";
import { useTRPC } from "@/trpc/client";
export function MyClient() {
const trpc = useTRPC();
const { data } = useSuspenseQuery(
trpc.greet.queryOptions({ name: "World" })
);
return {data.message};
}
Comparison with create-t3-app
create-t3-appis great for new projects.create-trpc-setupworks with existing projects, even if they already use Clerk, Shadcn UI, or custom providers. The command adds all necessary files without modifying your existing code.
Protected Procedure Helper
The generated init.ts includes a protectedProcedure that automatically throws an UNAUTHORIZED error when ctx.userId is missing:
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.userId) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({ ctx: { ...ctx, userId: ctx.userId } });
});
You can use it in any router:
getProfile: protectedProcedure.query(({ ctx }) => {
return { userId: ctx.userId }; // guaranteed non‑null
}),
Installation & Links
- npm package:
- GitHub repository:
If the CLI saved you time, consider starring the repository and sharing it with your team.