TRPC with Tanstack Query Integration
Backend Structure
- •Main router: trpc/routers/_app.ts - All routers must be registered here
- •Individual routers:
trpc/routers/*.router.ts- Create feature-specific routers (e.g.,chat.router.ts,vote.router.ts) - •Database queries: lib/db/queries.ts - All database operations should be defined here
- •Database schema: lib/db/schema.ts - Drizzle ORM schema definitions
Frontend Integration
Setup
- •Use
useTRPC()hook from@/trpc/react - •Import
useMutation,useQuery,useQueryClientfrom@tanstack/react-query
Query Pattern
typescript
const trpc = useTRPC();
const { data, isLoading, refetch } = useQuery({
...trpc.router.procedure.queryOptions(),
enabled: !!condition,
});
Mutation Pattern
typescript
const trpc = useTRPC();
const queryClient = useQueryClient();
const mutation = useMutation(
trpc.router.procedure.mutationOptions({
onSuccess: () => {
// Invalidate related queries
queryClient.invalidateQueries({
queryKey: trpc.router.relatedQuery.queryKey(),
});
},
})
);
// Usage
await mutation.mutateAsync({ input: data });
Backend Router Pattern
typescript
import { createTRPCRouter, protectedProcedure } from '@/trpc/init';
import { z } from 'zod';
export const featureRouter = createTRPCRouter({
queryName: protectedProcedure.query(async ({ ctx }) => {
return await dbFunction({ userId: ctx.user.id });
}),
mutationName: protectedProcedure
.input(z.object({ /* validation schema */ }))
.mutation(async ({ ctx, input }) => {
// Verify permissions/ownership
// Call database function
return await dbFunction(input);
}),
});
Key Principles
- •Always use
protectedProcedurefor authenticated routes - •Validate inputs with Zod schemas
- •Verify user ownership/permissions in mutations
- •Use database functions from lib/db/queries.ts
- •Invalidate related queries after mutations
- •Handle loading and error states in the UI