Next.js

next/server-action-checks

Every Server Action checks who is calling and what they may change, validates its input and returns only what the UI needs.

seriousmanualfe-nextjs

Why

A function marked 'use server' and exported is a public HTTP endpoint: anyone can call it with any arguments, whether or not a page shows the button. A check that lives only in the page, the layout or proxy.ts does not protect it, a FormData value can be anything, and whatever the action returns is serialized to the browser.

Fix

At the top of each action: read the session (const session = await auth()), refuse when it is missing, and check that the user may change this record (ownership or role). Parse the input with a schema (z.object({...}).safeParse(Object.fromEntries(formData))) and return field errors for useActionState. Return a small result ({ ok: true } or the errors), never a whole database row. Keep database access in a server-only data layer. Review by hand: a tool cannot tell whether the check is the right one.

Examples

// bad
'use server'
export async function deletePost(id: string) { await db.post.delete({ where: { id } }) }
// good
'use server'
export async function deletePost(id: unknown) {
  const session = await auth()
  const postId = PostId.parse(id)
  if (!session || !(await canEditPost(session.user, postId))) return { ok: false }
  await db.post.delete({ where: { id: postId } })
  revalidatePath('/posts')
  return { ok: true }
}

Sources

Facts

Id
next/server-action-checks
Severity
serious
Check
manual a person reviews it; the report lists it as a checklist
Applies to
jsx
Only in projects with
next
Taught by the skill
fe-nextjs Next.js App Router
Since version
0.1.0

Rule file on GitHub

Read it in the terminal

npx github:dawidolko/frontend-skills explain next/server-action-checks