Read
.agents/software-principles/SKILL.mdfirst.
Pre-Code Checklist
- Map data flow: origin (DB / external API / form input)
- Mutation strategy: Server Action (form/page-tied) vs. Route Handler (REST/webhook)
- Auth requirement at every server entry point
- All four UI states: loading · error · empty · ideal
Server/Client Boundary
| Need | Solution | Location |
|---|---|---|
| Fetch + render data | async Server Component |
app/**/page.jsx |
| Form / page mutation | Server Action 'use server' |
app/**/actions.js |
| REST endpoint / webhook | Route Handler | app/api/**/route.js |
| Interactivity / hooks | Client Component 'use client' — push to leaves |
components/ |
| Auth + route guard | Middleware | middleware.js |
| Shared business logic | Services / utilities | lib/ · services/ · utils/ |
Extensions: .jsx for JSX · .js everything else.
UI Layer
Apply all rules from frontend skill. Key additions:
- Pass only serializable, non-sensitive data as props to Client Components
- Wrap slow
asyncServer Components in<Suspense> error.jsxmust be'use client'redirect()throws — never call insidetry/catch
Server Actions (app/**/actions.js)
File must start with 'use server'. Required order:
- Validate input
- Check auth/session
- Call service layer — no business logic inline
revalidatePath()orrevalidateTag()after mutation- Return typed result — follow project convention for shape
Route Handlers (app/api/**/route.js)
Follow existing response shape convention. If none, pick one and apply consistently. Never mix shapes across handlers.
Required regardless of shape: success/error distinguishable · validation errors include field-level detail · no stack traces or internal paths exposed.
Handler order: validate input → auth check → service call → return envelope.
Data Layer (lib/db.js · services/*.js)
- All DB access in service layer — never inline in handlers or components
- Connection pooling always on. Parameterized queries or ORM — no string SQL
unstable_cache()orcache()for expensive repeated readsrevalidateTag()preferred overrevalidatePath()for precision
Security (non-negotiable)
- Auth check first in every Server Action and Route Handler — before any logic
middleware.jsguards routes viacookies()or token check- Secrets server-only — never
NEXT_PUBLIC_for sensitive values - Parameterized queries. Hash passwords (bcrypt/argon2)
- Rate-limit auth and mutation-heavy endpoints
Never Do
- DB queries or secrets in Client Components /
NEXT_PUBLIC_vars - Trust unvalidated input in actions or handlers
- Sensitive data as Client Component props (exposed in JS bundle)
- Business logic inline in route handlers or page components
- Skip any of the four UI states