Back to skills
SKILL.md
Trpc Api Limatechnologies Admin Telegram Bot 2
ASecuritytRPC end-to-end type-safe API patterns. Router setup, procedures, middleware, context, client configuration. Use when building type-safe APIs with tRPC.
- 666 stars
- 0 votes
- 0 copies
- 0 views
- Added September 2, 2026
Works with
Security analysis
100/100npx -y skills add majiayu000/claude-skill-registry --skill trpc-api-limatechnologies-admin-telegram-bot-2 --agent claude-codeAre you the author of Trpc Api Limatechnologies Admin Telegram Bot 2?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/majiayu000-trpc-api-limatechnologies-admin-telegram-bot-2)---
name: trpc-api
description: tRPC end-to-end type-safe API patterns. Router setup, procedures, middleware, context, client configuration. Use when building type-safe APIs with tRPC.
allowed-tools: Read, Write, Edit, Grep, Glob
---
# tRPC API - End-to-End Type Safety
## Purpose
Expert guidance for tRPC v11:
- **Router Setup** - Modular router architecture
- **Procedures** - Queries, mutations, subscriptions
- **Middleware** - Auth, logging, rate limiting
- **Context** - Session, database, utilities
- **Client** - React Query integration
---
## Project Structure
```
server/
├── trpc/
│ ├── index.ts # Router exports
│ ├── trpc.ts # tRPC instance
│ ├── context.ts # Context creation
│ └── routers/
│ ├── user.router.ts
│ ├── post.router.ts
│ └── _app.ts # Root router
app/
└── api/trpc/[trpc]/route.ts # Next.js handler
lib/
└── trpc/
├── client.ts # tRPC client
└── react.tsx # React Query provider
```
---
## Server Setup
### tRPC Instance
```typescript
// server/trpc/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import superjson from 'superjson';
import { type Context } from './context';
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 middleware = t.middleware;
```
### Context
```typescript
// server/trpc/context.ts
import { type CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { db } from '@db';
export async function createContext(opts: CreateNextContextOptions) {
const session = await getServerSession(authOptions);
return {
session,
user: session?.user ?? null,
db,
req: opts.req,
res: opts.res,
};
}
export type Context = Awaited<ReturnType<typeof createContext>>;
```
### Auth Middleware
```typescript
// server/trpc/trpc.ts
const isAuthed = middleware(({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'You must be logged in',
});
}
return next({
ctx: {
...ctx,
user: ctx.user, // User is now guaranteed
},
});
});
export const protectedProcedure = t.procedure.use(isAuthed);
```
---
## Router Patterns
### Basic Router
```typescript
// server/trpc/routers/user.router.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';
export const userRouter = router({
// Query - fetch data
getById: publicProcedure
.input(z.object({ id: z.string().uuid() }))
.query(async ({ input, ctx }) => {
const user = await ctx.db.user.findUnique({
where: { id: input.id },
select: { id: true, name: true, email: true },
});
if (!user) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'User not found',
});
}
return user;
}),
// Mutation - modify data
updateProfile: protectedProcedure
.input(
z.object({
name: z.string().min(2).max(100),
bio: z.string().max(500).optional(),
})
)
.mutation(async ({ input, ctx }) => {
return ctx.db.user.update({
where: { id: ctx.user.id },
data: input,
});
}),
// Me - current user
me: protectedProcedure.query(async ({ ctx }) => {
return ctx.db.user.findUnique({
where: { id: ctx.user.id },
});
}),
});
```
### Root Router
```typescript
// server/trpc/routers/_app.ts
import { router } from '../trpc';
import { userRouter } from './user.router';
import { postRouter } from './post.router';
export const appRouter = router({
user: userRouter,
post: postRouter,
});
export type AppRouter = typeof appRouter;
```
---
## Advanced Patterns
### Pagination
```typescript
const paginationSchema = z.object({
cursor: z.string().optional(),
limit: z.number().min(1).max(100).default(20),
});
export const postRouter = router({
list: publicProcedure.input(paginationSchema).query(async ({ input, ctx }) => {
const { cursor, limit } = input;
const posts = await ctx.db.post.findMany({
take: limit + 1,
cursor: cursor ? { id: cursor } : undefined,
orderBy: { createdAt: 'desc' },
});
let nextCursor: string | undefined;
if (posts.length > limit) {
const nextItem = posts.pop();
nextCursor = nextItem?.id;
}
return {
items: posts,
nextCursor,
};
}),
});
```
### Optimistic Updates
```typescript
// Client-side with React Query
const utils = trpc.useUtils();
const createPost = trpc.post.create.useMutation({
onMutate: async (newPost) => {
// Cancel outgoing refetches
await utils.post.list.cancel();
// Snapshot previous value
const previousPosts = utils.post.list.getData();
// Optimistically update
utils.post.list.setData(undefined, (old) => {
if (!old) return { items: [newPost], nextCursor: undefined };
return { ...old, items: [newPost, ...old.items] };
});
return { previousPosts };
},
onError: (err, newPost, context) => {
// Rollback on error
utils.post.list.setData(undefined, context?.previousPosts);
},
onSettled: () => {
// Always refetch after error or success
utils.post.list.invalidate();
},
});
```
### Batch Requests
```typescript
// tRPC automatically batches by default
// Multiple calls in same tick are batched
const user = trpc.user.me.useQuery();
const posts = trpc.post.list.useQuery();
const notifications = trpc.notification.unread.useQuery();
// These are batched into a single HTTP request
```
---
## Error Handling
### Error Codes
```typescript
throw new TRPCError({
code: 'NOT_FOUND', // 404
code: 'BAD_REQUEST', // 400
code: 'UNAUTHORIZED', // 401
code: 'FORBIDDEN', // 403
code: 'CONFLICT', // 409
code: 'INTERNAL_SERVER_ERROR', // 500
message: 'Descriptive message',
cause: originalError,
});
```
### Client Error Handling
```typescript
const mutation = trpc.post.create.useMutation({
onError: (error) => {
if (error.data?.code === 'CONFLICT') {
toast.error('Post with this title already exists');
} else if (error.data?.zodError) {
// Validation errors
const fieldErrors = error.data.zodError.fieldErrors;
Object.entries(fieldErrors).forEach(([field, errors]) => {
toast.error(`${field}: ${errors?.join(', ')}`);
});
} else {
toast.error(error.message);
}
},
});
```
---
## Client Setup
### React Provider
```typescript
// lib/trpc/react.tsx
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { createTRPCReact } from '@trpc/react-query';
import { useState } from 'react';
import superjson from 'superjson';
import { type AppRouter } from '@/server/trpc/routers/_app';
export const trpc = createTRPCReact<AppRouter>();
export function TRPCProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 1000,
refetchOnWindowFocus: false,
},
},
}));
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>
);
}
```
### Usage in Components
```typescript
'use client';
import { trpc } from '@/lib/trpc/react';
export function UserProfile() {
const { data: user, isLoading } = trpc.user.me.useQuery();
const updateProfile = trpc.user.updateProfile.useMutation();
if (isLoading) return <Skeleton />;
if (!user) return null;
return (
<form onSubmit={(e) => {
e.preventDefault();
updateProfile.mutate({ name: 'New Name' });
}}>
<input defaultValue={user.name} />
<button type="submit" disabled={updateProfile.isPending}>
Save
</button>
</form>
);
}
```
---
## Next.js Handler
```typescript
// app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/trpc/routers/_app';
import { createContext } from '@/server/trpc/context';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => createContext({ req }),
});
export { handler as GET, handler as POST };
```
---
## Agent Integration
This skill is used by:
- **trpc-expert** subagent
- **api-documenter** for API documentation
- **security-auditor** for route validation
- **test-coverage** for API tests
---
## FORBIDDEN
1. **User ID from input** - ALWAYS use `ctx.user.id`
2. **Procedures without `.input()`** - Validate all inputs
3. **`any` in input schemas** - Use proper Zod types
4. **Sensitive data in responses** - Filter with `.select()`
5. **Public procedures for mutations** - Use `protectedProcedure`
---
## Version
- **v1.0.0** - Initial implementation based on tRPC v11 patterns
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.