| name | scaffold-api |
| description | Scaffold a Next.js App Router API route (route.ts) for FixNow with zod validation, auth check via NextAuth, Drizzle ORM access, and proper error handling. Use when user says "tạo API endpoint X", "scaffold POST /api/appointments", "thêm route handler GET /api/parts". |
scaffold-api
Tạo API route mới theo pattern FixNow.
Hỏi user (nếu chưa rõ)
- HTTP method (GET / POST / PATCH / DELETE).
- Path (vd:
/api/appointments/[code]/status).
- Auth: none / customer / admin / owner-only?
- Input schema (body / query params)?
- Response shape mong muốn?
Reference docs/routes.md để xem endpoint nào đã định nghĩa sẵn.
Workflow
1. Tạo zod schema (nếu chưa có)
lib/validations/<feature>.ts:
import { z } from 'zod';
export const exampleSchema = z.object({
field1: z.string().min(1, 'Bắt buộc'),
field2: z.number().int().positive(),
});
export type ExampleInput = z.infer<typeof exampleSchema>;
2. Tạo route handler
app/api/<path>/route.ts:
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { eq } from 'drizzle-orm';
import { db } from '@/db';
import { someTable } from '@/db/schema';
import { auth } from '@/lib/auth';
import { exampleSchema } from '@/lib/validations/feature';
export async function POST(req: NextRequest) {
try {
const session = await auth();
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const body = await req.json();
const data = exampleSchema.parse(body);
const [row] = await db.insert(someTable).values(data).returning();
return NextResponse.json({ data: row }, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: 'Validation failed', details: error.flatten() },
{ status: 400 }
);
}
console.error('[POST /api/<path>]', error);
return NextResponse.json({ error: 'Internal error' }, { status: 500 });
}
}
3. Dynamic route params (Next.js 15+ async params)
export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ code: string }> }
) {
const { code } = await params;
}
4. Query string
const { searchParams } = req.nextUrl;
const status = searchParams.get('status');
const page = parseInt(searchParams.get('page') ?? '1', 10);
5. Read explicit columns (KHÔNG trả passwordHash)
const user = await db.query.users.findFirst({
where: eq(users.phone, phone),
columns: { id: true, fullName: true, phone: true, role: true },
});
6. Test bằng curl
curl -X POST http://localhost:3000/api/<path> \
-H "Content-Type: application/json" \
-d '{"field":"value"}'
Checklist
Pattern cho từng loại endpoint
| Loại | Auth | Response |
|---|
| Public GET | none | 200 |
| Create (POST) | tùy | 201 + body |
| Update (PATCH/PUT) | owner/admin | 200 + body |
| Delete | owner/admin | 204 (no body) |
| Login/register | none | 200 + session cookie |
Liên kết
- Phức tạp → spawn agent
fixnow-api-builder.
- Cần đổi schema trước → skill
db-migrate.