Type-safe APIs with tRPC v11 for Google Antigravity IDE
# tRPC v11 API Patterns for Google Antigravity
Master end-to-end type-safe APIs with tRPC v11 in Google Antigravity IDE. This comprehensive guide covers router creation, procedure definitions, middleware patterns, subscriptions, and client integration for building fully typed API layers without code generation.
## Configuration
Configure your Antigravity environment for tRPC:
```typescript
// .antigravity/trpc.ts
export const trpcConfig = {
version: "11.x",
features: {
subscriptions: true,
batching: true,
links: ["httpBatchLink", "wsLink"]
},
validation: "zod"
};
```
## Router Setup
Create type-safe routers:
```typescript
// server/trpc.ts
import { initTRPC, TRPCError } from "@trpc/server";
import { z } from "zod";
import superjson from "superjson";
type Context = {
user: User | null;
db: Database;
};
const t = initTRPC.context<Context>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError: error.cause instanceof z.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.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
...ctx,
user: ctx.user
}
});
});
```
## Procedure Definitions
Define input validation and handlers:
```typescript
// server/routers/user.ts
import { z } from "zod";
import { router, publicProcedure, protectedProcedure } from "../trpc";
export const userRouter = router({
getById: publicProcedure
.input(z.string().uuid())
.query(async ({ ctx, input }) => {
const user = await ctx.db.user.findUnique({
where: { id: input }
});
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found"
});
}
return user;
}),
list: publicProcedure
.input(z.object({
limit: z.number().min(1).max(100).default(10),
cursor: z.string().optional()
}))
.query(async ({ ctx, input }) => {
const items = await ctx.db.user.findMany({
take: input.limit + 1,
cursor: input.cursor ? { id: input.cursor } : undefined,
orderBy: { createdAt: "desc" }
});
let nextCursor: string | undefined;
if (items.length > input.limit) {
const nextItem = items.pop();
nextCursor = nextItem?.id;
}
return { items, nextCursor };
}),
create: protectedProcedure
.input(z.object({
name: z.string().min(1).max(100),
email: z.string().email()
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.user.create({
data: {
...input,
createdBy: ctx.user.id
}
});
}),
update: protectedProcedure
.input(z.object({
id: z.string().uuid(),
data: z.object({
name: z.string().min(1).max(100).optional(),
email: z.string().email().optional()
})
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.user.update({
where: { id: input.id },
data: input.data
});
})
});
```
## Middleware Patterns
Create reusable middleware:
```typescript
// server/middleware/rateLimit.ts
import { TRPCError } from "@trpc/server";
const rateLimitMap = new Map<string, { count: number; resetAt: number }>();
export const rateLimitMiddleware = t.middleware(async ({ ctx, next }) => {
const key = ctx.user?.id || ctx.ip;
const now = Date.now();
const limit = rateLimitMap.get(key);
if (limit && limit.resetAt > now && limit.count >= 100) {
throw new TRPCError({
code: "TOO_MANY_REQUESTS",
message: "Rate limit exceeded"
});
}
if (!limit || limit.resetAt <= now) {
rateLimitMap.set(key, { count: 1, resetAt: now + 60000 });
} else {
limit.count++;
}
return next();
});
// Logging middleware
export const loggerMiddleware = t.middleware(async ({ path, type, next }) => {
const start = Date.now();
const result = await next();
const duration = Date.now() - start;
console.log(`${type} ${path} - ${duration}ms`);
return result;
});
```
## Subscriptions
Implement real-time subscriptions:
```typescript
// server/routers/notifications.ts
import { observable } from "@trpc/server/observable";
import { EventEmitter } from "events";
const ee = new EventEmitter();
export const notificationRouter = router({
onNotification: protectedProcedure.subscription(({ ctx }) => {
return observable<Notification>((emit) => {
const onNotification = (data: Notification) => {
if (data.userId === ctx.user.id) {
emit.next(data);
}
};
ee.on("notification", onNotification);
return () => {
ee.off("notification", onNotification);
};
});
}),
send: protectedProcedure
.input(z.object({
userId: z.string(),
message: z.string()
}))
.mutation(async ({ input }) => {
const notification = {
id: crypto.randomUUID(),
...input,
createdAt: new Date()
};
ee.emit("notification", notification);
return notification;
})
});
```
## Client Integration
Use tRPC in React components:
```typescript
// utils/trpc.ts
import { createTRPCReact } from "@trpc/react-query";
import type { AppRouter } from "../server/routers";
export const trpc = createTRPCReact<AppRouter>();
// components/UserList.tsx
export function UserList() {
const { data, isLoading, fetchNextPage, hasNextPage } =
trpc.user.list.useInfiniteQuery(
{ limit: 10 },
{ getNextPageParam: (lastPage) => lastPage.nextCursor }
);
const utils = trpc.useUtils();
const createUser = trpc.user.create.useMutation({
onSuccess: () => {
utils.user.list.invalidate();
}
});
if (isLoading) return <div>Loading...</div>;
return (
<div>
{data?.pages.flatMap(page => page.items).map(user => (
<div key={user.id}>{user.name}</div>
))}
{hasNextPage && (
<button onClick={() => fetchNextPage()}>Load More</button>
)}
</div>
);
}
```
## Best Practices
Follow these guidelines for tRPC development:
1. **Organize by domain** - Group related procedures
2. **Use middleware** - Share common logic
3. **Validate inputs** - Always use Zod schemas
4. **Handle errors** - Throw TRPCError with codes
5. **Batch requests** - Use httpBatchLink
6. **Type exports** - Export router types for client
Google Antigravity IDE provides intelligent tRPC scaffolding and type inference assistance.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.