Create FasterCreate Faster
API

tRPC

Pre-configured tRPC v11 with end-to-end typesafe API routes, context creation, and optional TanStack Query integration.

Supported frameworks

→ tRPC Documentation

What create-faster adds

Files added:

src/trpc/
├── init.ts              # createTRPCContext() + procedure definitions
├── client.ts            # Standalone tRPC client with httpBatchLink
├── server.tsx           # Server-side utilities (RSC caller or Query proxy)
├── query-client.ts      # QueryClient factory (if TanStack Query)
├── providers.tsx        # TRPCReactProvider (if TanStack Query)
└── routers/
    ├── _app.ts          # Root router combining all routers
    └── hello.ts         # Example router with Zod validation

src/app/api/trpc/
└── [trpc]/route.ts      # Fetch adapter API handler

# Turborepo only:
packages/api/
├── src/
│   ├── index.ts         # Public exports
│   ├── trpc.ts          # Context + procedures
│   ├── root.ts          # Root router
│   └── router/
│       └── hello.ts     # Example router
├── package.json         # @repo/api
└── tsconfig.json

init.ts

Context creation and procedure definitions. Automatically includes db if ORM is selected, and session + protectedProcedure if Better Auth is installed:

src/trpc/init.ts
import { db } from '@/lib/db';
import { initTRPC, TRPCError } from '@trpc/server';
import superjson from 'superjson';

export async function createTRPCContext() {
  return {
    db,
  };
}

type TRPCContext = Awaited<ReturnType<typeof createTRPCContext>>;

const t = initTRPC.context<TRPCContext>().create({
  transformer: superjson,
});

export const router = t.router;
export const publicProcedure = t.procedure;

With Better Auth, the context includes session and a protectedProcedure middleware is exported:

export const protectedProcedure = publicProcedure.use(async (opts) => {
  if (!opts.ctx.session?.user) {
    throw new TRPCError({
      code: 'UNAUTHORIZED',
      message: 'You must be logged in to access this resource',
    });
  }

  return opts.next({
    ctx: {
      ...opts.ctx,
      session: opts.ctx.session,
      user: opts.ctx.session.user,
    },
  });
});

routers/hello.ts

Example router with Zod validation:

src/trpc/routers/hello.ts
import { z } from 'zod';
import { publicProcedure, router } from '@/trpc/init';

export const helloRouter = router({
  greet: publicProcedure
    .input(z.object({ name: z.string().min(1) }))
    .query(({ input }) => {
      return { greeting: `Hello, ${input.name}!` };
    }),
});

routers/_app.ts

Root router combining all routers with type exports:

src/trpc/routers/_app.ts
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import { router } from '@/trpc/init';
import { helloRouter } from './hello';

export const appRouter = router({
  hello: helloRouter,
});

export type AppRouter = typeof appRouter;
export type RouterInput = inferRouterInputs<AppRouter>;
export type RouterOutput = inferRouterOutputs<AppRouter>;

server.tsx

Server-side utilities for RSC. Behavior depends on whether TanStack Query is selected:

With TanStack Query — provides createTRPCOptionsProxy for RSC queries with hydration:

src/trpc/server.tsx
import 'server-only';
import type React from 'react';
import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
import { createTRPCOptionsProxy, type TRPCQueryOptions } from '@trpc/tanstack-react-query';
import { cache } from 'react';
import { makeQueryClient } from '@/trpc/query-client';
import { appRouter } from '@/trpc/routers/_app';
import { createTRPCContext } from '@/trpc/init';

const getQueryClient = cache(makeQueryClient);

export const trpc = createTRPCOptionsProxy({
  ctx: createTRPCContext,
  router: appRouter,
  queryClient: getQueryClient,
});

export function HydrateClient(props: { children: React.ReactNode }) {
  const queryClient = getQueryClient();
  const state = dehydrate(queryClient);
  return <HydrationBoundary state={state}>{props.children}</HydrationBoundary>;
}

export function prefetch<T extends ReturnType<TRPCQueryOptions<any>>>(queryOptions: T) {
  const queryClient = getQueryClient();
  if (queryOptions.queryKey[1]?.type === 'infinite') {
    void queryClient.prefetchInfiniteQuery(queryOptions as any);
  } else {
    void queryClient.prefetchQuery(queryOptions);
  }
}

Without TanStack Query — provides a direct caller for server-side procedure calls:

src/trpc/server.tsx
import 'server-only';
import { appRouter } from '@/trpc/routers/_app';
import { createTRPCContext } from '@/trpc/init';

export const trpc = appRouter.createCaller(createTRPCContext);

Turborepo mode: Creates @repo/api package depending on @repo/db and @repo/auth. App imports from @repo/api. All @/trpc imports become @repo/api imports.

On this page