Home / Packages / @cursor-directory/trpc-official

@cursor-directory/trpc-official

Featured

Guidelines for writing Next.js apps with tRPC v11, enabling end-to-end typesafe APIs without schemas

prpm install @cursor-directory/trpc-official
0 total downloads

šŸ“„ Full Prompt Content

---
description: Guidelines for writing Next.js apps with tRPC
globs: "**/*.ts, **/*.tsx, **/*.js, **/*.jsx"
---

## Overview

[tRPC](https://trpc.io/) enables end-to-end typesafe APIs, allowing you to build and consume APIs without schemas, code generation, or runtime errors. These rules will help you follow best practices for tRPC v11.

## Project Structure

For a clean tRPC setup, follow this recommended structure:
```
.
ā”œā”€ā”€ src
│   ā”œā”€ā”€ pages
│   │   ā”œā”€ā”€ _app.tsx  # add `createTRPCNext` setup here
│   │   ā”œā”€ā”€ api
│   │   │   └── trpc
│   │   │       └── [trpc].ts  # tRPC HTTP handler
│   │   ā”œā”€ā”€ server
│   │   │   ā”œā”€ā”€ routers
│   │   │   │   ā”œā”€ā”€ _app.ts  # main app router
│   │   │   │   ā”œā”€ā”€ [feature].ts  # feature-specific routers
│   │   │   │   └── [...]
│   │   │   ā”œā”€ā”€ context.ts   # create app context
│   │   │   └── trpc.ts      # procedure helpers
│   │   └── utils
│   │       └── trpc.ts  # typesafe tRPC hooks
```

## Server-Side Setup

### Initialize tRPC Backend

```typescript
// server/trpc.ts
import { initTRPC } from '@trpc/server';

// Initialize tRPC backend (should be done once per backend)
const t = initTRPC.create();

// Export reusable router and procedure helpers
export const router = t.router;
export const publicProcedure = t.procedure;
```

### Create Router

```typescript
// server/routers/_app.ts
import { z } from 'zod';
import { router, publicProcedure } from '../trpc';

export const appRouter = router({
  // Your procedures here
  greeting: publicProcedure
    .input(z.object({ name: z.string() }))
    .query(({ input }) => {
      return `Hello ${input.name}`;
    }),
});

// Export type definition of API (not the router itself!)
export type AppRouter = typeof appRouter;
```

## Client-Side Setup

### Next.js Integration

```typescript
// utils/trpc.ts
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '../server/routers/_app';

function getBaseUrl() {
  if (typeof window !== 'undefined') return ''; // browser should use relative path
  if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
  return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
}

export const trpc = createTRPCNext<AppRouter>({
  config() {
    return {
      links: [
        httpBatchLink({
          url: `${getBaseUrl()}/api/trpc`,
          // Include auth headers when needed
          async headers() {
            return {
              // authorization: getAuthCookie(),
            };
          },
        }),
      ],
    };
  },
  ssr: false, // Set to true if you want to use server-side rendering
});
```

## Best Practices

1. **Use Zod for Input Validation**: Always validate procedure inputs with Zod for better type safety and runtime validation.

    ```typescript
    import { z } from 'zod';
    
    procedure
      .input(z.object({ 
        id: z.string().uuid(),
        email: z.string().email(),
        age: z.number().min(18) 
      }))
      .mutation(({ input }) => { /* your code */ })
    ```

2. **Organize Routers by Feature**: Split your routers into logical domains/features rather than having one large router.

    ```typescript
    // server/routers/user.ts
    export const userRouter = router({
      list: publicProcedure.query(() => { /* ... */ }),
      byId: publicProcedure.input(z.string()).query(({ input }) => { /* ... */ }),
      create: publicProcedure.input(/* ... */).mutation(({ input }) => { /* ... */ }),
    });
    
    // server/routers/_app.ts
    import { userRouter } from './user';
    import { postRouter } from './post';
    
    export const appRouter = router({
      user: userRouter,
      post: postRouter,
    });
    ```

3. **Use Middleware for Common Logic**: Apply middleware for authentication, logging, or other cross-cutting concerns.

    ```typescript
    const isAuthed = t.middleware(({ next, ctx }) => {
      if (!ctx.user) {
        throw new TRPCError({ code: 'UNAUTHORIZED' });
      }
      return next({
        ctx: {
          // Add user information to context
          user: ctx.user,
        },
      });
    });
    
    const protectedProcedure = t.procedure.use(isAuthed);
    ```

4. **Use Proper Error Handling**: Utilize tRPC's error handling for consistent error responses.

    ```typescript
    import { TRPCError } from '@trpc/server';
    
    publicProcedure
      .input(z.string())
      .query(({ input }) => {
        const user = getUserById(input);
        if (!user) {
          throw new TRPCError({
            code: 'NOT_FOUND',
            message: `User with id ${input} not found`,
          });
        }
        return user;
      });
    ```

5. **Use Data Transformers**: Use SuperJSON for automatic handling of dates, Maps, Sets, etc.

    ```typescript
    import { initTRPC } from '@trpc/server';
    import superjson from 'superjson';
    
    const t = initTRPC.create({
      transformer: superjson,
    });
    ```

6. **Leverage React Query Integration**: For React projects, use tRPC's React Query utilities for data fetching, mutations, and caching.

    ```tsx
    function UserProfile({ userId }: { userId: string }) {
      const { data, isLoading, error } = trpc.user.byId.useQuery(userId);
      
      if (isLoading) return <div>Loading...</div>;
      if (error) return <div>Error: {error.message}</div>;
      
      return <div>{data.name}</div>;
    }
    ```

7. **Context Creation**: Create a proper context object to share resources across procedures.

    ```typescript
    // server/context.ts
    import { inferAsyncReturnType } from '@trpc/server';
    import * as trpcNext from '@trpc/server/adapters/next';
    import { prisma } from './prisma';
    
    export async function createContext({
      req,
      res,
    }: trpcNext.CreateNextContextOptions) {
      const user = await getUser(req);
      return {
        req,
        res,
        prisma,
        user,
      };
    }
    
    export type Context = inferAsyncReturnType<typeof createContext>;
    ```

8. **Type Exports**: Only export types, not the actual router implementations, from your server code to client code.

    ```typescript
    // Export type router type signature, NOT the router itself
    export type AppRouter = typeof appRouter;
    ```

9. **Procedure Types**: Use different procedure types for different authorization levels.

    ```typescript
    export const publicProcedure = t.procedure;
    export const protectedProcedure = t.procedure.use(isAuthed);
    export const adminProcedure = t.procedure.use(isAdmin);
    ```

10. **Performance Optimization**: Use batching and prefetching for optimized data loading.

    ```typescript
    // Client-side batching in Next.js setup
    httpBatchLink({
      url: `${getBaseUrl()}/api/trpc`,
      maxURLLength: 2083,
    })
    
    // Prefetching data in Next.js
    export async function getStaticProps() {
      const ssg = createServerSideHelpers({
        router: appRouter,
        ctx: {},
      });
      
      await ssg.post.byId.prefetch('1');
      
      return {
        props: {
          trpcState: ssg.dehydrate(),
        },
        revalidate: 1,
      };
    }
    ```

## Version Compatibility

This guide is for tRPC v11, which requires:
- TypeScript >= 5.7.2
- Strict TypeScript mode (`"strict": true` in tsconfig.json)

## Further Resources

- [Official Documentation](https://trpc.io/docs)
- [GitHub Repository](https://github.com/trpc/trpc)
- [Example Apps](https://trpc.io/docs/example-apps)

šŸ’” Suggested Test Inputs

Loading suggested inputs...

šŸŽÆ Community Test Results

Loading results...

šŸ“¦ Package Info

Format
cursor
Type
rule
Category
Backend

šŸ”— Links