A fullstack web application with separate frontend and backend.
| Layer | Technology |
|---|---|
| Frontend | React 19 + TypeScript + Tailwind CSS |
| Backend | Node.js + Express (or Next.js API Routes) |
| Database | PostgreSQL + Prisma ORM |
| Auth | NextAuth.js v5 |
| Testing | Vitest + React Testing Library |
| CI/CD | GitHub Actions |
src/
app/ # Next.js App Router (RSC by default)
components/ # Shared UI components
lib/
db.ts # Prisma client singleton
auth.ts # Auth configuration
validators/ # Zod schemas
actions/ # Server Actions
types/ # TypeScript type definitions
prisma/
schema.prisma # Database schema
- Strict mode enabled (
"strict": true) - No
any— useunknownand narrow - Prefer
interfacefor object shapes,typefor unions/intersections - Use Zod for runtime validation at system boundaries
- Server Components by default. Add
"use client"only when needed - Colocate component, test, and styles in the same directory
- Use Server Actions for mutations — no API routes for form handling
- All schema changes via Prisma migrations (
npx prisma migrate dev) - Never use raw SQL unless Prisma cannot express the query
- Index foreign keys and frequently filtered columns
- Validate all user input with Zod before database operations
- Use parameterized queries (Prisma handles this automatically)
- Never expose internal IDs in URLs — use slugs or UUIDs
- Set
httpOnly,secure,sameSiteon all cookies
npm run dev # Start dev server (Turbopack)
npm run build # Production build
npm test # Run Vitest
npm run lint # ESLint
npm run db:push # Push schema changes (dev only)
npm run db:migrate # Create and apply migration
npm run db:studio # Open Prisma Studio- Branch naming:
feat/short-description,fix/issue-number - Commit format: conventional commits
- Always run
npm test && npm run lintbefore committing - Squash merge to main
- Do NOT commit
.envfiles - Do NOT use
eval()ordangerouslySetInnerHTML - Do NOT store passwords in plain text
- Do NOT skip TypeScript errors with
@ts-ignore