AEAl-Andalus⺢Experience
Sign in · Join
AEAl-Andalus⺢Experience
ProjectsGalleryAboutArticlesDashboardAPI
© 2026 — Modular content systems☕buy me a coffee
Framework Study Guide
ChaptersCardsLegendRoadmapRoute Map
Chapters
00 · Root Layout01 · Server Page (page.tsx)01a · Special Files (not-found, error, loading)02 · Client Component (UI & Interaction)02a · Client Boundaries & Islands03 · Entity Actions (actions/*.ts)04 · Server Actions ("use server")05 · Context Provider (context/*.tsx)06 · API Route (app/(api)/<entity>/route.ts)07 · TypeScript + Prisma08 · Entity-First Feature Design09 · Deployment, Docker & Cloud10 · Next 16 + Prisma 7 Upgrade11 · Prisma 7 (SQLite-first, App Router)12 · TypeORM (Entities + Migrations)13 · Drizzle ORM (SQL-first)11 · CSS, Mobile-First Flex, Tailwind12 · Libraries, Accelerators & Production Shortcuts14 · Next.js 16: cache, PPR, proxy, AI
→ cards/03-entity-actions

Chapter

03 · Entity Actions (actions/*.ts)

Server authority over data: Prisma lives here; validate, authorize, and expose get*/mutations.

Mental Model

  • Single source of truth for this entity’s data.
  • Encapsulates all Prisma access; framework-agnostic.
  • Called by Server Pages (reads) and Server Actions (writes).

File Classification

Layer: Entity data authority
Runtime: Server only
Prisma: ✅ Yes (only here)
UI Logic: ❌ Never
Server Actions: ❌ (separate files)
revalidatePath: ❌ (unless intentionally exposed)

Should Do

  • Read/write via Prisma.
  • Validate inputs; enforce authorization.
  • Expose get* reads and mutation helpers for Server Actions to call.
  • Remain framework-agnostic (no JSX, no client imports).

Should Not (and where it belongs)

  • UI logic → client components.
  • Context/hooks → client or provider files.
  • revalidatePath calls → server actions or route handlers that orchestrate mutations.
  • Internal API fetch → not needed; call Prisma directly here.

Canonical Example — Prisma 7 Adapter Pattern

import { getPrisma } from "@/lib/prisma";
import type Project from "@/types/project";

export async function getProjects(): Promise<Project[]> {
return getPrisma().project.findMany({ orderBy: { createdAt: "desc" } }); // read
}

export async function getProjectById(id: string): Promise<Project | null> {
return getPrisma().project.findUnique({ where: { id } }); // guarded read
}

export async function createProject(data: { name: string; ownerId: string; }) {
if (!data.name.trim()) throw new Error("Name required"); // security/validation
return getPrisma().project.create({ data }); // write
}

Colour-Coded Miniature

import { getPrisma } from "@/lib/prisma"; // 🟡 adapter singleton

export async function getProjectById(id: string) {
return getPrisma().project.findUnique({ where: { id } }); // 🟡
}

Quick Rules

  • All Prisma lives here; nowhere else.
  • Validate and authorize at the edge of the entity.
  • No UI, no hooks, no JSX.
  • Server Actions call these for writes; Server Pages call these for reads.