autoworkflow 3.1.4 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/analyze.md +19 -0
- package/.claude/commands/audit.md +174 -11
- package/.claude/commands/build.md +39 -0
- package/.claude/commands/commit.md +25 -0
- package/.claude/commands/fix.md +23 -0
- package/.claude/commands/plan.md +18 -0
- package/.claude/commands/suggest.md +23 -0
- package/.claude/commands/verify.md +18 -0
- package/.claude/hooks/post-bash-router.sh +20 -0
- package/.claude/hooks/post-commit.sh +140 -0
- package/.claude/hooks/pre-edit.sh +129 -0
- package/.claude/hooks/session-check.sh +79 -0
- package/.claude/settings.json +40 -6
- package/.claude/settings.local.json +3 -1
- package/.claude/skills/actix.md +337 -0
- package/.claude/skills/alembic.md +504 -0
- package/.claude/skills/angular.md +237 -0
- package/.claude/skills/api-design.md +187 -0
- package/.claude/skills/aspnet-core.md +377 -0
- package/.claude/skills/astro.md +245 -0
- package/.claude/skills/auth-clerk.md +327 -0
- package/.claude/skills/auth-firebase.md +367 -0
- package/.claude/skills/auth-nextauth.md +359 -0
- package/.claude/skills/auth-supabase.md +368 -0
- package/.claude/skills/axum.md +386 -0
- package/.claude/skills/blazor.md +456 -0
- package/.claude/skills/chi.md +348 -0
- package/.claude/skills/code-review.md +133 -0
- package/.claude/skills/csharp.md +296 -0
- package/.claude/skills/css-modules.md +325 -0
- package/.claude/skills/cypress.md +343 -0
- package/.claude/skills/debugging.md +133 -0
- package/.claude/skills/diesel.md +392 -0
- package/.claude/skills/django.md +301 -0
- package/.claude/skills/docker.md +319 -0
- package/.claude/skills/doctrine.md +473 -0
- package/.claude/skills/documentation.md +182 -0
- package/.claude/skills/dotnet.md +409 -0
- package/.claude/skills/drizzle.md +293 -0
- package/.claude/skills/echo.md +321 -0
- package/.claude/skills/eloquent.md +256 -0
- package/.claude/skills/emotion.md +426 -0
- package/.claude/skills/entity-framework.md +370 -0
- package/.claude/skills/express.md +316 -0
- package/.claude/skills/fastapi.md +329 -0
- package/.claude/skills/fastify.md +299 -0
- package/.claude/skills/fiber.md +315 -0
- package/.claude/skills/flask.md +322 -0
- package/.claude/skills/gin.md +342 -0
- package/.claude/skills/git.md +116 -0
- package/.claude/skills/github-actions.md +353 -0
- package/.claude/skills/go.md +377 -0
- package/.claude/skills/gorm.md +409 -0
- package/.claude/skills/graphql.md +478 -0
- package/.claude/skills/hibernate.md +379 -0
- package/.claude/skills/hono.md +306 -0
- package/.claude/skills/java.md +400 -0
- package/.claude/skills/jest.md +313 -0
- package/.claude/skills/jpa.md +282 -0
- package/.claude/skills/kotlin.md +347 -0
- package/.claude/skills/kubernetes.md +363 -0
- package/.claude/skills/laravel.md +414 -0
- package/.claude/skills/mcp-browser.md +320 -0
- package/.claude/skills/mcp-database.md +219 -0
- package/.claude/skills/mcp-fetch.md +241 -0
- package/.claude/skills/mcp-filesystem.md +204 -0
- package/.claude/skills/mcp-github.md +217 -0
- package/.claude/skills/mcp-memory.md +240 -0
- package/.claude/skills/mcp-search.md +218 -0
- package/.claude/skills/mcp-slack.md +262 -0
- package/.claude/skills/micronaut.md +388 -0
- package/.claude/skills/mongodb.md +319 -0
- package/.claude/skills/mongoose.md +355 -0
- package/.claude/skills/mysql.md +281 -0
- package/.claude/skills/nestjs.md +335 -0
- package/.claude/skills/nextjs-app-router.md +260 -0
- package/.claude/skills/nextjs-pages.md +172 -0
- package/.claude/skills/nuxt.md +202 -0
- package/.claude/skills/openapi.md +489 -0
- package/.claude/skills/performance.md +199 -0
- package/.claude/skills/php.md +398 -0
- package/.claude/skills/playwright.md +371 -0
- package/.claude/skills/postgresql.md +257 -0
- package/.claude/skills/prisma.md +293 -0
- package/.claude/skills/pydantic.md +304 -0
- package/.claude/skills/pytest.md +313 -0
- package/.claude/skills/python.md +272 -0
- package/.claude/skills/quarkus.md +377 -0
- package/.claude/skills/react.md +230 -0
- package/.claude/skills/redis.md +391 -0
- package/.claude/skills/refactoring.md +143 -0
- package/.claude/skills/remix.md +246 -0
- package/.claude/skills/rest-api.md +490 -0
- package/.claude/skills/rocket.md +366 -0
- package/.claude/skills/rust.md +341 -0
- package/.claude/skills/sass.md +380 -0
- package/.claude/skills/sea-orm.md +382 -0
- package/.claude/skills/security.md +167 -0
- package/.claude/skills/sequelize.md +395 -0
- package/.claude/skills/spring-boot.md +416 -0
- package/.claude/skills/sqlalchemy.md +269 -0
- package/.claude/skills/sqlx-rust.md +408 -0
- package/.claude/skills/state-jotai.md +346 -0
- package/.claude/skills/state-mobx.md +353 -0
- package/.claude/skills/state-pinia.md +431 -0
- package/.claude/skills/state-redux.md +337 -0
- package/.claude/skills/state-tanstack-query.md +434 -0
- package/.claude/skills/state-zustand.md +340 -0
- package/.claude/skills/styled-components.md +403 -0
- package/.claude/skills/svelte.md +238 -0
- package/.claude/skills/sveltekit.md +207 -0
- package/.claude/skills/symfony.md +437 -0
- package/.claude/skills/tailwind.md +279 -0
- package/.claude/skills/terraform.md +394 -0
- package/.claude/skills/testing-library.md +371 -0
- package/.claude/skills/trpc.md +426 -0
- package/.claude/skills/typeorm.md +368 -0
- package/.claude/skills/vitest.md +330 -0
- package/.claude/skills/vue.md +202 -0
- package/.claude/skills/warp.md +365 -0
- package/README.md +135 -52
- package/package.json +1 -1
- package/system/triggers.md +152 -11
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# tRPC Skill
|
|
2
|
+
|
|
3
|
+
## Project Setup
|
|
4
|
+
\`\`\`typescript
|
|
5
|
+
// server/trpc.ts
|
|
6
|
+
import { initTRPC, TRPCError } from '@trpc/server';
|
|
7
|
+
import { type Context } from './context';
|
|
8
|
+
import superjson from 'superjson';
|
|
9
|
+
import { ZodError } from 'zod';
|
|
10
|
+
|
|
11
|
+
const t = initTRPC.context<Context>().create({
|
|
12
|
+
transformer: superjson,
|
|
13
|
+
errorFormatter({ shape, error }) {
|
|
14
|
+
return {
|
|
15
|
+
...shape,
|
|
16
|
+
data: {
|
|
17
|
+
...shape.data,
|
|
18
|
+
zodError:
|
|
19
|
+
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const router = t.router;
|
|
26
|
+
export const publicProcedure = t.procedure;
|
|
27
|
+
export const middleware = t.middleware;
|
|
28
|
+
export const mergeRouters = t.mergeRouters;
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
## Context
|
|
32
|
+
\`\`\`typescript
|
|
33
|
+
// server/context.ts
|
|
34
|
+
import { type inferAsyncReturnType } from '@trpc/server';
|
|
35
|
+
import { type CreateNextContextOptions } from '@trpc/server/adapters/next';
|
|
36
|
+
import { getServerSession } from 'next-auth';
|
|
37
|
+
import { prisma } from './db';
|
|
38
|
+
import { authOptions } from './auth';
|
|
39
|
+
|
|
40
|
+
export async function createContext(opts: CreateNextContextOptions) {
|
|
41
|
+
const session = await getServerSession(opts.req, opts.res, authOptions);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
prisma,
|
|
45
|
+
session,
|
|
46
|
+
user: session?.user,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type Context = inferAsyncReturnType<typeof createContext>;
|
|
51
|
+
\`\`\`
|
|
52
|
+
|
|
53
|
+
## Middleware
|
|
54
|
+
\`\`\`typescript
|
|
55
|
+
// server/middleware.ts
|
|
56
|
+
import { TRPCError } from '@trpc/server';
|
|
57
|
+
import { middleware, publicProcedure } from './trpc';
|
|
58
|
+
|
|
59
|
+
// Logging middleware
|
|
60
|
+
const loggerMiddleware = middleware(async ({ path, type, next }) => {
|
|
61
|
+
const start = Date.now();
|
|
62
|
+
const result = await next();
|
|
63
|
+
const duration = Date.now() - start;
|
|
64
|
+
console.log(\`[\${type}] \${path} - \${duration}ms\`);
|
|
65
|
+
return result;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Auth middleware
|
|
69
|
+
const isAuthed = middleware(async ({ ctx, next }) => {
|
|
70
|
+
if (!ctx.session || !ctx.user) {
|
|
71
|
+
throw new TRPCError({
|
|
72
|
+
code: 'UNAUTHORIZED',
|
|
73
|
+
message: 'Not authenticated',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return next({
|
|
77
|
+
ctx: {
|
|
78
|
+
...ctx,
|
|
79
|
+
user: ctx.user, // user is now non-nullable
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Role middleware
|
|
85
|
+
const hasRole = (role: string) =>
|
|
86
|
+
middleware(async ({ ctx, next }) => {
|
|
87
|
+
if (ctx.user?.role !== role) {
|
|
88
|
+
throw new TRPCError({
|
|
89
|
+
code: 'FORBIDDEN',
|
|
90
|
+
message: 'Insufficient permissions',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return next();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Procedures with middleware
|
|
97
|
+
export const protectedProcedure = publicProcedure
|
|
98
|
+
.use(loggerMiddleware)
|
|
99
|
+
.use(isAuthed);
|
|
100
|
+
|
|
101
|
+
export const adminProcedure = protectedProcedure.use(hasRole('admin'));
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
## Router Definition
|
|
105
|
+
\`\`\`typescript
|
|
106
|
+
// server/routers/user.ts
|
|
107
|
+
import { z } from 'zod';
|
|
108
|
+
import { router, publicProcedure, protectedProcedure } from '../trpc';
|
|
109
|
+
import { TRPCError } from '@trpc/server';
|
|
110
|
+
|
|
111
|
+
export const userRouter = router({
|
|
112
|
+
// Public query
|
|
113
|
+
getById: publicProcedure
|
|
114
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
115
|
+
.query(async ({ ctx, input }) => {
|
|
116
|
+
const user = await ctx.prisma.user.findUnique({
|
|
117
|
+
where: { id: input.id },
|
|
118
|
+
select: {
|
|
119
|
+
id: true,
|
|
120
|
+
name: true,
|
|
121
|
+
image: true,
|
|
122
|
+
// Don't expose email publicly
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (!user) {
|
|
127
|
+
throw new TRPCError({
|
|
128
|
+
code: 'NOT_FOUND',
|
|
129
|
+
message: 'User not found',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return user;
|
|
134
|
+
}),
|
|
135
|
+
|
|
136
|
+
// Protected query
|
|
137
|
+
me: protectedProcedure.query(async ({ ctx }) => {
|
|
138
|
+
return ctx.prisma.user.findUnique({
|
|
139
|
+
where: { id: ctx.user.id },
|
|
140
|
+
include: { profile: true },
|
|
141
|
+
});
|
|
142
|
+
}),
|
|
143
|
+
|
|
144
|
+
// Mutation with input validation
|
|
145
|
+
updateProfile: protectedProcedure
|
|
146
|
+
.input(
|
|
147
|
+
z.object({
|
|
148
|
+
name: z.string().min(2).max(100).optional(),
|
|
149
|
+
bio: z.string().max(500).optional(),
|
|
150
|
+
website: z.string().url().optional().or(z.literal('')),
|
|
151
|
+
})
|
|
152
|
+
)
|
|
153
|
+
.mutation(async ({ ctx, input }) => {
|
|
154
|
+
return ctx.prisma.user.update({
|
|
155
|
+
where: { id: ctx.user.id },
|
|
156
|
+
data: input,
|
|
157
|
+
});
|
|
158
|
+
}),
|
|
159
|
+
|
|
160
|
+
// List with pagination
|
|
161
|
+
list: publicProcedure
|
|
162
|
+
.input(
|
|
163
|
+
z.object({
|
|
164
|
+
limit: z.number().min(1).max(100).default(10),
|
|
165
|
+
cursor: z.string().uuid().optional(),
|
|
166
|
+
})
|
|
167
|
+
)
|
|
168
|
+
.query(async ({ ctx, input }) => {
|
|
169
|
+
const { limit, cursor } = input;
|
|
170
|
+
|
|
171
|
+
const users = await ctx.prisma.user.findMany({
|
|
172
|
+
take: limit + 1,
|
|
173
|
+
...(cursor && {
|
|
174
|
+
cursor: { id: cursor },
|
|
175
|
+
skip: 1,
|
|
176
|
+
}),
|
|
177
|
+
orderBy: { createdAt: 'desc' },
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
let nextCursor: string | undefined;
|
|
181
|
+
if (users.length > limit) {
|
|
182
|
+
const nextItem = users.pop();
|
|
183
|
+
nextCursor = nextItem?.id;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { users, nextCursor };
|
|
187
|
+
}),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// server/routers/post.ts
|
|
191
|
+
export const postRouter = router({
|
|
192
|
+
create: protectedProcedure
|
|
193
|
+
.input(
|
|
194
|
+
z.object({
|
|
195
|
+
title: z.string().min(1).max(200),
|
|
196
|
+
content: z.string().min(1),
|
|
197
|
+
published: z.boolean().default(false),
|
|
198
|
+
})
|
|
199
|
+
)
|
|
200
|
+
.mutation(async ({ ctx, input }) => {
|
|
201
|
+
return ctx.prisma.post.create({
|
|
202
|
+
data: {
|
|
203
|
+
...input,
|
|
204
|
+
authorId: ctx.user.id,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}),
|
|
208
|
+
|
|
209
|
+
delete: protectedProcedure
|
|
210
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
211
|
+
.mutation(async ({ ctx, input }) => {
|
|
212
|
+
const post = await ctx.prisma.post.findUnique({
|
|
213
|
+
where: { id: input.id },
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (!post) {
|
|
217
|
+
throw new TRPCError({ code: 'NOT_FOUND' });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (post.authorId !== ctx.user.id) {
|
|
221
|
+
throw new TRPCError({ code: 'FORBIDDEN' });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return ctx.prisma.post.delete({ where: { id: input.id } });
|
|
225
|
+
}),
|
|
226
|
+
});
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
## App Router
|
|
230
|
+
\`\`\`typescript
|
|
231
|
+
// server/routers/_app.ts
|
|
232
|
+
import { router } from '../trpc';
|
|
233
|
+
import { userRouter } from './user';
|
|
234
|
+
import { postRouter } from './post';
|
|
235
|
+
|
|
236
|
+
export const appRouter = router({
|
|
237
|
+
user: userRouter,
|
|
238
|
+
post: postRouter,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
export type AppRouter = typeof appRouter;
|
|
242
|
+
\`\`\`
|
|
243
|
+
|
|
244
|
+
## Next.js API Handler
|
|
245
|
+
\`\`\`typescript
|
|
246
|
+
// app/api/trpc/[trpc]/route.ts (App Router)
|
|
247
|
+
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
|
|
248
|
+
import { appRouter } from '@/server/routers/_app';
|
|
249
|
+
import { createContext } from '@/server/context';
|
|
250
|
+
|
|
251
|
+
const handler = (req: Request) =>
|
|
252
|
+
fetchRequestHandler({
|
|
253
|
+
endpoint: '/api/trpc',
|
|
254
|
+
req,
|
|
255
|
+
router: appRouter,
|
|
256
|
+
createContext: () => createContext({ req }),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export { handler as GET, handler as POST };
|
|
260
|
+
\`\`\`
|
|
261
|
+
|
|
262
|
+
## Client Setup
|
|
263
|
+
\`\`\`typescript
|
|
264
|
+
// utils/trpc.ts
|
|
265
|
+
import { createTRPCReact } from '@trpc/react-query';
|
|
266
|
+
import { type AppRouter } from '@/server/routers/_app';
|
|
267
|
+
|
|
268
|
+
export const trpc = createTRPCReact<AppRouter>();
|
|
269
|
+
|
|
270
|
+
// app/providers.tsx
|
|
271
|
+
'use client';
|
|
272
|
+
|
|
273
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
274
|
+
import { httpBatchLink } from '@trpc/client';
|
|
275
|
+
import { useState } from 'react';
|
|
276
|
+
import { trpc } from '@/utils/trpc';
|
|
277
|
+
import superjson from 'superjson';
|
|
278
|
+
|
|
279
|
+
export function TRPCProvider({ children }: { children: React.ReactNode }) {
|
|
280
|
+
const [queryClient] = useState(() => new QueryClient());
|
|
281
|
+
const [trpcClient] = useState(() =>
|
|
282
|
+
trpc.createClient({
|
|
283
|
+
links: [
|
|
284
|
+
httpBatchLink({
|
|
285
|
+
url: '/api/trpc',
|
|
286
|
+
transformer: superjson,
|
|
287
|
+
}),
|
|
288
|
+
],
|
|
289
|
+
})
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
|
294
|
+
<QueryClientProvider client={queryClient}>
|
|
295
|
+
{children}
|
|
296
|
+
</QueryClientProvider>
|
|
297
|
+
</trpc.Provider>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
\`\`\`
|
|
301
|
+
|
|
302
|
+
## Client Usage
|
|
303
|
+
\`\`\`typescript
|
|
304
|
+
// components/UserProfile.tsx
|
|
305
|
+
'use client';
|
|
306
|
+
|
|
307
|
+
import { trpc } from '@/utils/trpc';
|
|
308
|
+
|
|
309
|
+
export function UserProfile({ userId }: { userId: string }) {
|
|
310
|
+
// Query
|
|
311
|
+
const { data: user, isLoading, error } = trpc.user.getById.useQuery(
|
|
312
|
+
{ id: userId },
|
|
313
|
+
{ enabled: !!userId }
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
// Mutation
|
|
317
|
+
const utils = trpc.useUtils();
|
|
318
|
+
const updateProfile = trpc.user.updateProfile.useMutation({
|
|
319
|
+
onSuccess: () => {
|
|
320
|
+
// Invalidate and refetch
|
|
321
|
+
utils.user.me.invalidate();
|
|
322
|
+
},
|
|
323
|
+
onError: (error) => {
|
|
324
|
+
console.error('Update failed:', error.message);
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const handleUpdate = (data: { name: string }) => {
|
|
329
|
+
updateProfile.mutate(data);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
if (isLoading) return <div>Loading...</div>;
|
|
333
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
334
|
+
|
|
335
|
+
return <div>{user?.name}</div>;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Infinite query
|
|
339
|
+
export function PostList() {
|
|
340
|
+
const {
|
|
341
|
+
data,
|
|
342
|
+
fetchNextPage,
|
|
343
|
+
hasNextPage,
|
|
344
|
+
isFetchingNextPage,
|
|
345
|
+
} = trpc.post.list.useInfiniteQuery(
|
|
346
|
+
{ limit: 10 },
|
|
347
|
+
{
|
|
348
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<div>
|
|
354
|
+
{data?.pages.map((page) =>
|
|
355
|
+
page.posts.map((post) => <PostCard key={post.id} post={post} />)
|
|
356
|
+
)}
|
|
357
|
+
{hasNextPage && (
|
|
358
|
+
<button
|
|
359
|
+
onClick={() => fetchNextPage()}
|
|
360
|
+
disabled={isFetchingNextPage}
|
|
361
|
+
>
|
|
362
|
+
{isFetchingNextPage ? 'Loading...' : 'Load More'}
|
|
363
|
+
</button>
|
|
364
|
+
)}
|
|
365
|
+
</div>
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
\`\`\`
|
|
369
|
+
|
|
370
|
+
## Server-Side Calls (RSC)
|
|
371
|
+
\`\`\`typescript
|
|
372
|
+
// Server component usage
|
|
373
|
+
import { createCaller } from '@/server/routers/_app';
|
|
374
|
+
import { createContext } from '@/server/context';
|
|
375
|
+
|
|
376
|
+
export default async function Page() {
|
|
377
|
+
const ctx = await createContext();
|
|
378
|
+
const caller = createCaller(ctx);
|
|
379
|
+
|
|
380
|
+
const user = await caller.user.me();
|
|
381
|
+
|
|
382
|
+
return <div>{user.name}</div>;
|
|
383
|
+
}
|
|
384
|
+
\`\`\`
|
|
385
|
+
|
|
386
|
+
## Error Handling
|
|
387
|
+
\`\`\`typescript
|
|
388
|
+
// Custom error with data
|
|
389
|
+
throw new TRPCError({
|
|
390
|
+
code: 'BAD_REQUEST',
|
|
391
|
+
message: 'Validation failed',
|
|
392
|
+
cause: new Error('Original error'),
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Error codes
|
|
396
|
+
type TRPCErrorCode =
|
|
397
|
+
| 'PARSE_ERROR'
|
|
398
|
+
| 'BAD_REQUEST'
|
|
399
|
+
| 'UNAUTHORIZED'
|
|
400
|
+
| 'FORBIDDEN'
|
|
401
|
+
| 'NOT_FOUND'
|
|
402
|
+
| 'METHOD_NOT_SUPPORTED'
|
|
403
|
+
| 'TIMEOUT'
|
|
404
|
+
| 'CONFLICT'
|
|
405
|
+
| 'PRECONDITION_FAILED'
|
|
406
|
+
| 'PAYLOAD_TOO_LARGE'
|
|
407
|
+
| 'TOO_MANY_REQUESTS'
|
|
408
|
+
| 'CLIENT_CLOSED_REQUEST'
|
|
409
|
+
| 'INTERNAL_SERVER_ERROR';
|
|
410
|
+
\`\`\`
|
|
411
|
+
|
|
412
|
+
## ❌ DON'T
|
|
413
|
+
- Skip input validation with Zod
|
|
414
|
+
- Expose sensitive data in public procedures
|
|
415
|
+
- Forget to handle errors in mutations
|
|
416
|
+
- Use any types for inputs/outputs
|
|
417
|
+
- Skip middleware for auth checks
|
|
418
|
+
|
|
419
|
+
## ✅ DO
|
|
420
|
+
- Use superjson for Date/Map/Set support
|
|
421
|
+
- Create reusable middleware
|
|
422
|
+
- Use proper error codes
|
|
423
|
+
- Invalidate queries after mutations
|
|
424
|
+
- Use infinite queries for pagination
|
|
425
|
+
- Type your context properly
|
|
426
|
+
- Use procedure builders for common patterns
|