Claude Skills Guide

Claude Code for tRPC React Query Workflow

Building type-safe APIs with tRPC combined with React Query (TanStack Query) creates a powerful full-stack TypeScript development experience. However, setting up this workflow efficiently and maintaining it as your application grows requires understanding the integration points. In this guide, I’ll show you how Claude Code can accelerate your tRPC and React Query development workflow.

Understanding the tRPC and React Query Integration

tRPC automatically generates TypeScript types from your backend procedures, and React Query handles the client-side data fetching and caching. When used together, you get end-to-end type safety without any code generation step. The key insight is that tRPC’s useQuery and useMutation hooks are built on top of React Query, giving you all its powerful features out of the box.

The integration typically looks like this in your client setup:

// lib/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server/router';

export const trpc = createTRPCReact<AppRouter>();

Then in your main app component, you wrap your application with the tRPC provider and React Query client:

// App.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { useState } from 'react';
import { trpc } from './lib/trpc';

function App() {
  const [queryClient] = useState(() => new QueryClient());
  const [trpcClient] = useState(() =>
    trpc.createClient({
      links: [
        httpBatchLink({
          url: '/api/trpc',
        }),
      ],
    })
  );

  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        <YourApp />
      </QueryClientProvider>
    </trpc.Provider>
  );
}

Using Claude Code to Generate tRPC Procedures

One of the most valuable ways Claude Code helps with this workflow is by generating boilerplate code for your tRPC routers and procedures. When you need to create a new feature, you can describe what you want in natural language and let Claude generate the corresponding backend and frontend code.

For example, when you need to create a user management feature, you can ask Claude Code to generate the router:

// server/routers/user.ts
import { z } from 'zod';
import { router, protectedProcedure } from '../trpc';

export const userRouter = router({
  getAll: protectedProcedure.query(async ({ ctx }) => {
    return ctx.prisma.user.findMany({
      select: { id: true, email: true, name: true },
    });
  }),

  getById: protectedProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      return ctx.prisma.user.findUnique({
        where: { id: input.id },
      });
    }),

  create: protectedProcedure
    .input(z.object({
      email: z.string().email(),
      name: z.string().min(1),
    }))
    .mutation(async ({ ctx, input }) => {
      return ctx.prisma.user.create({
        data: input,
      });
    }),

  update: protectedProcedure
    .input(z.object({
      id: z.string(),
      email: z.string().email().optional(),
      name: z.string().min(1).optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input;
      return ctx.prisma.user.update({
        where: { id },
        data,
      });
    }),

  delete: protectedProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      return ctx.prisma.user.delete({
        where: { id: input.id },
      });
    }),
});

Creating React Query Components with Claude Code

Once your tRPC procedures are defined, Claude Code can help you create the React components that consume them. It understands the types that tRPC generates, so the code it produces is fully typed.

For a user list component, Claude Code can generate:

// components/UserList.tsx
import { trpc } from '../lib/trpc';

export function UserList() {
  const { data: users, isLoading, error } = trpc.user.getAll.useQuery();

  if (isLoading) return <div>Loading users...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {users?.map((user) => (
        <li key={user.id}>
          {user.name} ({user.email})
        </li>
      ))}
    </ul>
  );
}

For mutations, Claude Code can help you set up proper optimistic updates and error handling:

// components/CreateUser.tsx
import { useState } from 'react';
import { trpc } from '../lib/trpc';

export function CreateUser() {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const utils = trpc.useUtils();

  const createUser = trpc.user.create.useMutation({
    onSuccess: () => {
      utils.user.getAll.invalidate();
      setEmail('');
      setName('');
    },
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    createUser.mutate({ email, name });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
        required
      />
      <button type="submit" disabled={createUser.isPending}>
        {createUser.isPending ? 'Creating...' : 'Create User'}
      </button>
    </form>
  );
}

Optimizing Your Workflow with Claude Code

Here are practical tips for getting the most out of Claude Code in your tRPC React Query workflow:

1. Define your router structure first

Before generating components, ensure your tRPC router is well-structured. Describe your data model to Claude Code and ask it to generate a complete router with CRUD operations. This establishes the types that will flow through your application.

2. Use the context parameter for dependent queries

When you have queries that depend on other data, use tRPC’s context:

// In your router
getUserPosts: protectedProcedure
  .input(z.object({ userId: z.string() }))
  .query(async ({ ctx, input }) => {
    return ctx.prisma.post.findMany({
      where: { authorId: input.userId },
    });
  }),

Then in your component, invalidate the related query after mutations:

const createPost = trpc.post.create.useMutation({
  onSuccess: () => {
    utils.post.getUserPosts.invalidate({ userId: currentUserId });
  },
});

3. Leverage React Query’s caching strategies

Claude Code can help you configure appropriate stale times and caching behavior:

const { data } = trpc.user.getAll.useQuery(undefined, {
  staleTime: 5 * 60 * 1000, // 5 minutes
  refetchOnWindowFocus: false,
});

4. Set up proper error handling

Always handle errors gracefully in your components:

const { data, error, isError } = trpc.user.getById.useQuery({ id });

if (isError) {
  return (
    <div className="error">
      Failed to load user: {error.message}
    </div>
  );
}

Best Practices for Type Safety

To maximize the benefits of the tRPC React Query workflow, follow these best practices that Claude Code can help you implement:

Conclusion

Claude Code significantly accelerates tRPC and React Query development by generating type-safe boilerplate code, helping you set up proper caching strategies, and ensuring your components handle loading and error states correctly. The key is to first establish a well-structured router, then let Claude Code generate the consuming components with proper types and best practices built in.

By following the patterns in this guide, you’ll build more maintainable applications with less boilerplate code and stronger type safety throughout your full-stack TypeScript application.

Built by theluckystrike — More at zovo.one