nextjs
Next.js Project Architecture Rules
Scope: Project-specific policies and architecture decisions only.
Version: Next.js 15.5+ with App Router
1. BFF Architecture (Mandatory)
Absolute Rules
Next.js serves ONLY as a thin Backend for Frontend (BFF) layer:
Browser ↔ Next.js Server ↔ Backend API ↔ Database
NEVER:
- ❌ Direct database access from Next.js (no Prisma, no ORMs)
- ❌ Business logic implementation in Next.js
- ❌ Data validation beyond input sanitization
ALWAYS:
- ✅ All business logic in separate backend service
- ✅ All database operations via backend API
- ✅ Next.js for: SSR/SSG, API aggregation, session management, caching
2. Component Strategy (Enforced)
Server Components First
Rule: Default to Server Components. 'use client' only at leaf nodes.
Client Component allowed for:
- Event handlers (onClick, onChange)
- Browser APIs (localStorage, window)
- React hooks (useState, useEffect)
Violation: Client Component wrapping Server Components
3. Rendering Strategy (Explicit Declaration Required)
Mandatory Export
Every page MUST explicitly declare rendering intent:
// Required - choose one:
export const dynamic = "force-static"; // SSG
export const dynamic = "force-dynamic"; // SSR
export const revalidate = 3600; // ISR
No implicit rendering. Always be explicit about caching behavior.
4. Data Fetching (Server Actions vs API Routes)
Server Actions (Default for Internal Operations)
Use for:
- Form submissions
- Data mutations
- Internal Next.js operations
Location: app/actions/*.ts or inline with 'use server'
API Routes (External Integration ONLY)
Use for:
- Webhooks (Stripe, GitHub, etc.)
- OAuth callbacks
- Mobile app endpoints
- Third-party service integrations
Location: app/api/*/route.ts
NEVER: API routes for internal Next.js-to-Next.js communication
5. Caching Policy (Explicit Intent Required)
Mandatory Cache Declaration
All fetch calls MUST explicitly specify caching:
// Required - choose one:
fetch(url, { next: { revalidate: 3600 } }); // Time-based
fetch(url, { cache: "no-store" }); // Dynamic
Use React cache() to prevent duplicate requests within render cycle.
No implicit caching. Always declare intent.
Critical Violations
- Direct DB access from Next.js → Architecture violation
- API Routes for internal mutations → Use Server Actions
- Missing rendering strategy declaration → Add explicit export
- Client Component not at leaf → Move
'use client'down - Implicit caching → Add explicit cache declaration
- Backend not separated → Mandatory separate service