Lesson 15 of 81
A Server Action looks like a normal function call from a Client Component, but it's actually a POST request to wherever that function is defined — and that fact is the whole reason auth checks belong inside it, not just in your UI or your proxy.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
Where can the 'use server' directive be placed?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
The 'use server' directive marks a function to run only on the server, even though it can be imported and called from a Client Component like any other function. It can go two places: at the top of a file (every export in that file becomes server-only), or inline at the top of one async function inside a Server Component.
A function marked this way is a Server Function. When it's specifically used to handle a form submission or mutation, it's called a Server Action — the convention is passing it to <form action={fn}> or <button formAction={fn}>. React wraps that call in a transition automatically.
The part that's easy to miss: <form action={fn}> isn't a JavaScript event listener pretending to be a form. It's a real HTML form submission — which is why it works via progressive enhancement, functioning even before the page's JavaScript has finished loading or hydrating, because the browser can submit it as a plain POST either way. Under the hood, a Server Action is a POST request to the route where the function is defined.
// app/posts/actions.ts
"use server";
import { auth } from "@/lib/auth";
import { revalidatePath } from "next/cache";
export async function deletePost(postId: string) {
const session = await auth();
if (!session?.user) {
throw new Error("Unauthorized");
}
await db.post.delete({ where: { id: postId, authorId: session.user.id } });
revalidatePath("/posts");
}
// app/posts/[id]/delete-button.tsx
"use client";
import { deletePost } from "../actions";
export function DeleteButton({ postId }: { postId: string }) {
return (
<form action={deletePost.bind(null, postId)}>
<button type="submit">Delete</button>
</form>
);
}
deletePost only ever runs on the server, but it's imported straight into a Client Component and bound to a <form>. The delete button works even on a slow connection before hydration finishes, then upgrades seamlessly once React takes over. But because this action is really just a POST endpoint at whatever route app/posts/actions.ts compiles to, it's reachable directly — not only through this specific button. That's why deletePost checks auth() itself, instead of assuming that only this one properly-guarded button could ever call it.
'use server' scopes to either a whole file or a single inline function — same directive, different placement, different scope. A file-level directive marks every export; an inline one marks just that function.'use server'-marked function; Server Action specifically means one used for a mutation via a form or transition.<form action={fn}> submits as a real HTML form before hydration. This is what makes it work with JS disabled or not yet loaded — it's not a useEffect-driven illusion, it's the browser doing an actual form POST.proxy.ts matcher excluding a path also excludes any Server Function calls on that path — auth belongs inside the function, not only upstream of it.'use server' can't live inside a 'use client' file — the function has to be defined in a server file and imported."Walk me through what actually happens on the network when this form gets submitted, and what's actually protecting that delete action" tests whether a candidate knows Server Actions are real POST endpoints reachable outside the rendered UI — and therefore need their own auth check, not just a guarded button or a proxy rule that happens to cover the page.