Master tRPC for building end-to-end type-safe APIs. Learn procedures, routers, middleware, error handling, subscriptions, and integration with Next.js App Router for seamless full-stack TypeScript development.
# tRPC Type-Safe API Development
Build end-to-end type-safe APIs with tRPC, eliminating the need for manual type definitions and API documentation while ensuring compile-time safety across your entire stack.
## Understanding tRPC Architecture
tRPC revolutionizes API development by sharing types between your backend and frontend without code generation. This approach provides instant type inference, autocompletion, and compile-time error checking.
### Router and Procedure Setup
```typescript
// server/trpc.ts
import { initTRPC, TRPCError } from "@trpc/server";
import { type Context } from "./context";
import superjson from "superjson";
import { ZodError } from "zod";
const t = initTRPC.context<Context>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session?.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
session: { ...ctx.session, user: ctx.session.user },
},
});
});
```
### Creating Type-Safe Routers
```typescript
// server/routers/posts.ts
import { z } from "zod";
import { router, publicProcedure, protectedProcedure } from "../trpc";
export const postsRouter = router({
getAll: publicProcedure
.input(
z.object({
limit: z.number().min(1).max(100).default(10),
cursor: z.string().nullish(),
category: z.string().optional(),
})
)
.query(async ({ ctx, input }) => {
const posts = await ctx.db.post.findMany({
take: input.limit + 1,
where: input.category ? { category: input.category } : undefined,
cursor: input.cursor ? { id: input.cursor } : undefined,
orderBy: { createdAt: "desc" },
include: { author: { select: { name: true, image: true } } },
});
let nextCursor: typeof input.cursor = undefined;
if (posts.length > input.limit) {
const nextItem = posts.pop();
nextCursor = nextItem!.id;
}
return { posts, nextCursor };
}),
create: protectedProcedure
.input(
z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
category: z.string(),
published: z.boolean().default(false),
})
)
.mutation(async ({ ctx, input }) => {
return ctx.db.post.create({
data: {
...input,
authorId: ctx.session.user.id,
},
});
}),
update: protectedProcedure
.input(
z.object({
id: z.string(),
title: z.string().min(1).max(200).optional(),
content: z.string().min(1).optional(),
published: z.boolean().optional(),
})
)
.mutation(async ({ ctx, input }) => {
const post = await ctx.db.post.findUnique({
where: { id: input.id },
});
if (!post || post.authorId !== ctx.session.user.id) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Not authorized to update this post",
});
}
return ctx.db.post.update({
where: { id: input.id },
data: input,
});
}),
});
```
### Next.js App Router Integration
```typescript
// app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@/server/routers/_app";
import { createContext } from "@/server/context";
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext,
onError: ({ error, path }) => {
console.error(`tRPC error on ${path}:`, error);
},
});
export { handler as GET, handler as POST };
```
### React Query Integration
```typescript
// lib/trpc/client.ts
import { createTRPCReact } from "@trpc/react-query";
import { type AppRouter } from "@/server/routers/_app";
export const trpc = createTRPCReact<AppRouter>();
// components/providers/TRPCProvider.tsx
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink } from "@trpc/client";
import { useState } from "react";
import { trpc } from "@/lib/trpc/client";
import superjson from "superjson";
export function TRPCProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: "/api/trpc",
transformer: superjson,
}),
],
})
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</trpc.Provider>
);
}
```
### Optimistic Updates and Mutations
```typescript
// hooks/useCreatePost.ts
import { trpc } from "@/lib/trpc/client";
export function useCreatePost() {
const utils = trpc.useUtils();
return trpc.posts.create.useMutation({
onMutate: async (newPost) => {
await utils.posts.getAll.cancel();
const previousData = utils.posts.getAll.getData();
utils.posts.getAll.setData({ limit: 10 }, (old) => ({
posts: [
{ ...newPost, id: "temp", createdAt: new Date(), author: null },
...(old?.posts ?? []),
],
nextCursor: old?.nextCursor,
}));
return { previousData };
},
onError: (err, newPost, context) => {
utils.posts.getAll.setData({ limit: 10 }, context?.previousData);
},
onSettled: () => {
utils.posts.getAll.invalidate();
},
});
}
```
This comprehensive tRPC setup provides complete type safety from database to UI, with automatic inference eliminating manual type synchronization.This trpc prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their trpc implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For trpc projects, consider mentioning your framework version, coding style, and any specific libraries you're using.