

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# T3 Stack — Cursor Rules2# Comprehensive rules for T3 Stack (Next.js + tRPC + Prisma + NextAuth)34## Project Context5You are working on a T3 Stack application — the full-stack TypeScript framework combining6Next.js App Router, tRPC for type-safe APIs, Prisma for database access, and NextAuth.js7for authentication. The codebase prioritizes end-to-end type safety, with types flowing8from database schema through API layer to the frontend without manual type definitions.910## Tech Stack11- Next.js 14+ (App Router)12- tRPC v11 for type-safe API layer13- Prisma ORM for database (PostgreSQL recommended)14- NextAuth.js / Auth.js for authentication15- Tailwind CSS for styling16- TypeScript (strict mode — non-negotiable)17- Zod for input validation18- React Query (via tRPC) for server state1920## Coding Style2122### Naming Conventions23- tRPC routers: camelCase (e.g., `userRouter`, `postRouter`)24- tRPC procedures: camelCase verbs (e.g., `getById`, `create`, `updateStatus`)25- Prisma models: PascalCase singular (e.g., `User`, `Post`, `Comment`)26- Components: PascalCase (e.g., `PostCard`, `UserAvatar`)27- Server utilities: camelCase in `src/server/`28- Client hooks: `use` prefix (e.g., `useCreatePost`)2930### Project Structure31```32src/33 app/34 (auth)/35 sign-in/page.tsx36 (dashboard)/37 layout.tsx38 page.tsx39 posts/40 [id]/page.tsx41 api/42 trpc/[trpc]/route.ts # tRPC HTTP handler43 auth/[...nextauth]/route.ts44 layout.tsx45 page.tsx46 server/47 api/48 root.ts # Root tRPC router49 trpc.ts # tRPC initialization, context, middleware50 routers/51 user.ts52 post.ts53 auth.ts # NextAuth configuration54 db.ts # Prisma client singleton55 trpc/56 react.tsx # tRPC React client setup57 server.ts # tRPC server caller58 components/59 ui/ # Shared UI components60 posts/ # Feature-specific components61 lib/62 utils.ts63 validators.ts # Shared Zod schemas64prisma/65 schema.prisma66```6768## tRPC Patterns6970### Router Definition71```ts72// server/api/routers/post.ts73import { z } from "zod";74import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";75import { TRPCError } from "@trpc/server";7677export const postRouter = createTRPCRouter({78 getAll: publicProcedure79 .input(z.object({80 limit: z.number().min(1).max(100).default(20),81 cursor: z.string().nullish(),82 }))83 .query(async ({ ctx, input }) => {84 const posts = await ctx.db.post.findMany({85 take: input.limit + 1,86 cursor: input.cursor ? { id: input.cursor } : undefined,87 orderBy: { createdAt: "desc" },88 include: { author: { select: { name: true, image: true } } },89 });90 let nextCursor: string | undefined;91 if (posts.length > input.limit) {92 nextCursor = posts.pop()!.id;93 }94 return { posts, nextCursor };95 }),9697 create: protectedProcedure98 .input(z.object({99 title: z.string().min(1).max(200),100 content: z.string().min(1),101 }))102 .mutation(async ({ ctx, input }) => {103 return ctx.db.post.create({104 data: { ...input, authorId: ctx.session.user.id },105 });106 }),107108 delete: protectedProcedure109 .input(z.object({ id: z.string() }))110 .mutation(async ({ ctx, input }) => {111 const post = await ctx.db.post.findUnique({ where: { id: input.id } });112 if (!post) throw new TRPCError({ code: "NOT_FOUND" });113 if (post.authorId !== ctx.session.user.id) {114 throw new TRPCError({ code: "FORBIDDEN" });115 }116 return ctx.db.post.delete({ where: { id: input.id } });117 }),118});119```120121### tRPC Context and Middleware122```ts123// server/api/trpc.ts124import { initTRPC, TRPCError } from "@trpc/server";125import superjson from "superjson";126import { getServerSession } from "next-auth";127import { authOptions } from "../auth";128import { db } from "../db";129130const createTRPCContext = async (opts: { headers: Headers }) => {131 const session = await getServerSession(authOptions);132 return { db, session, ...opts };133};134135const t = initTRPC.context<typeof createTRPCContext>().create({136 transformer: superjson,137 errorFormatter({ shape, error }) {138 return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null } };139 },140});141142export const createTRPCRouter = t.router;143export const publicProcedure = t.procedure;144export const protectedProcedure = t.procedure.use(({ ctx, next }) => {145 if (!ctx.session?.user) throw new TRPCError({ code: "UNAUTHORIZED" });146 return next({ ctx: { session: { ...ctx.session, user: ctx.session.user } } });147});148```149150### Client-Side Usage151```tsx152"use client";153import { api } from "~/trpc/react";154155export function PostList() {156 const { data, fetchNextPage, hasNextPage, isLoading } = api.post.getAll.useInfiniteQuery(157 { limit: 20 },158 { getNextPageParam: (lastPage) => lastPage.nextCursor },159 );160161 const utils = api.useUtils();162 const createPost = api.post.create.useMutation({163 onSuccess: () => {164 utils.post.getAll.invalidate(); // Refetch after mutation165 },166 });167168 // ...169}170```171172### Server-Side Calling173```tsx174// In a Server Component — call tRPC directly without HTTP175import { api } from "~/trpc/server";176177export default async function PostPage({ params }: { params: { id: string } }) {178 const post = await api.post.getById({ id: params.id });179 return <PostDetail post={post} />;180}181```182183## Prisma Patterns184- Define clear relations with `@relation` and explicit foreign keys185- Use `select` or `include` to control returned fields — never return everything186- Use transactions for multi-table mutations: `db.$transaction([])`187- Create seed data in `prisma/seed.ts`188- Run migrations with `npx prisma migrate dev --name descriptive_name`189- Use `@map` and `@@map` for custom table/column names matching SQL conventions190- Add indexes on frequently queried/filtered columns191192## Authentication193- Use NextAuth.js with the Prisma adapter194- Access session in tRPC context — never pass user info from the client195- Use `protectedProcedure` for all authenticated endpoints196- Check resource ownership in procedures (not just authentication)197- Use middleware for role-based access control198199## Error Handling200- Use `TRPCError` with appropriate codes: `NOT_FOUND`, `UNAUTHORIZED`, `FORBIDDEN`, `BAD_REQUEST`201- Validate all inputs with Zod schemas — tRPC integrates natively202- Handle Prisma errors (unique constraint, not found) and map to TRPCError203- Use React error boundaries for client-side error display204- Format Zod errors in the tRPC error formatter for client consumption205206## Testing207- Test tRPC procedures by creating a test caller with mocked context208- Test Prisma queries against a test database (or use prismock)209- Use Playwright for E2E tests210- Test auth flows with mocked sessions211212## Performance Guidelines213- Use cursor-based pagination (not offset) for large datasets214- Prefetch data in Server Components using the server-side tRPC caller215- Use React Query's `staleTime` to reduce unnecessary refetches216- Use `select` in Prisma to fetch only needed fields217- Implement optimistic updates for mutations that affect UI immediately218- Use `Suspense` boundaries for streaming server-rendered content219220## Common Pitfalls221- Importing server code in client components (breaks the build)222- Not invalidating queries after mutations (stale UI)223- Using offset pagination for large tables (slow on large offsets)224- Returning full Prisma objects with sensitive fields to the client225- Not checking resource ownership — just checking authentication is not enough226- Forgetting `superjson` transformer (dates, Maps, Sets won't serialize correctly)227- Not wrapping client providers properly in the root layout228- Using `getServerSession` outside of RSC/route handlers (wrong context)229
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| survivorforge/cursor-rulesrules/ai-ml-python/.cursorrules · 17 | .cursorrules | teststylearchdeployment+2 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/api-microservices/.cursorrules · 17 | .cursorrules | buildteststylearch+5 | 92/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/database-sql/.cursorrules · 17 | .cursorrules | styletypessecuritydatabase+3 | 65/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/devops-docker/.cursorrules · 17 | .cursorrules | setupbuildteststyle+4 | 93/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/devops-infrastructure/.cursorrules · 17 | .cursorrules | buildteststylesecurity+3 | 93/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/django-rest/.cursorrules · 17 | .cursorrules | buildteststylearch+5 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/docker-devops/.cursorrules · 17 | .cursorrules | setupteststylearch+6 | 85/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/flutter-dart/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 89/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/go-gin/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+5 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/langchain-ai/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+4 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/mobile-react-native/.cursorrules · 17 | .cursorrules | teststylearchtypes+7 | 89/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-14-app-router/.cursorrules · 17 | .cursorrules | teststyletypestesting-strategy+3 | 71/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-app-router/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-typescript/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nodejs-express-typescript/.cursorrules · 17 | .cursorrules | setupteststylearch+7 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nodejs-express/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+7 | 92/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/performance-optimization/.cursorrules · 17 | .cursorrules | styledatabaseapiperformance+2 | 65/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/python-django/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+6 | 99/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/python-fastapi/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+6 | 99/100 | 13 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/survivorforge-cursor-rules-rules-t3-stack-cursorrules)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.