Loading...
Loading...
Scaffold a new Next.js route with tRPC router, Zod validation, and proper file structure
npx skill4agent add cerico/macfair scaffold-routeapp/[feature]/
├── page.tsx # Server component
├── loading.tsx # Skeleton loader
├── error.tsx # Error boundary
└── components/
└── index.ts # Barrel export
server/api/routers/[feature]/
└── index.ts # tRPC router with CRUD procedures
validations/
└── [feature].ts # Zod schemas + inferred typesserver/api/root.tsvalidations/index.tsimport { Suspense } from 'react'
import { FeatureList } from './components'
import { FeatureSkeleton } from '@/components/skeletons'
export default function FeaturePage() {
return (
<main className="container py-8">
<h1 className="text-2xl font-bold mb-6">Features</h1>
<Suspense fallback={<FeatureSkeleton />}>
<FeatureList />
</Suspense>
</main>
)
}import { FeatureSkeleton } from '@/components/skeletons'
export default function Loading() {
return <FeatureSkeleton />
}'use client'
interface Props {
error: Error & { digest?: string }
reset: () => void
}
export default function Error({ error, reset }: Props) {
return (
<main className="container py-8">
<h1 className="text-2xl font-bold mb-4">Something went wrong</h1>
<p className="text-muted-foreground mb-4">{error.message}</p>
<button onClick={reset} className="text-primary underline">
Try again
</button>
</main>
)
}import { z } from 'zod'
export const createFeatureSchema = z.object({
name: z.string().min(1, 'Name is required'),
})
export const updateFeatureSchema = createFeatureSchema.partial()
export type CreateFeatureInput = z.infer<typeof createFeatureSchema>
export type UpdateFeatureInput = z.infer<typeof updateFeatureSchema>import { z } from 'zod'
import { router, publicProcedure } from '@/server/api/trpc'
import { createFeatureSchema, updateFeatureSchema } from '@/validations'
import { prisma } from '@/prisma/prisma'
export const featureRouter = router({
list: publicProcedure.query(async () => {
return prisma.feature.findMany({
select: { id: true, name: true, createdAt: true },
orderBy: { createdAt: 'desc' },
})
}),
get: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return prisma.feature.findUniqueOrThrow({
where: { id: input.id },
select: { id: true, name: true, createdAt: true },
})
}),
create: publicProcedure
.input(createFeatureSchema)
.mutation(async ({ input }) => {
return prisma.feature.create({
data: input,
select: { id: true },
})
}),
update: publicProcedure
.input(z.object({ id: z.string(), data: updateFeatureSchema }))
.mutation(async ({ input }) => {
return prisma.feature.update({
where: { id: input.id },
data: input.data,
select: { id: true },
})
}),
delete: publicProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ input }) => {
return prisma.feature.delete({
where: { id: input.id },
select: { id: true },
})
}),
})import { featureRouter } from './routers/feature'
export const appRouter = router({
// existing routers...
feature: featureRouter,
})Note: Prisma model scaffolding is currently under review. For now, create models manually inand run migrations before using this skill.prisma/schema.prisma
@/any