Server Actions & Mutations
5 questions found
What is a server action in Next.js, and what problem does it solve?
Beginner
A server action is a function marked with a use server directive that runs only on the server but can be called directly from a client component or a form, letting you update data, such as saving a form submission, without needing to manually create a separate API route just to handle that one operation.
'use server';
export async function addTodo(formData) {
const text = formData.get('text');
await db.todo.create({ data: { text } });
}
Real-world example
A simple todo application lets users add new tasks by submitting a form directly to a server action, without the developer needing to build and maintain a separate API endpoint just for creating todos.
Common follow-ups: Where should the use server directive be placed in a file?;Can a server action be called from a regular button click as well as a form submission?
Form Handling & Validation in Next.js;Route Handlers (API Route.js)
How do you call a server action directly from a button click inside a client component, rather than through a form submission?
Intermediate
You import the server action function into your client component and call it directly inside an event handler, such as onClick, just like you would call any other asynchronous function, and Next.js handles sending that call to the server behind the scenes.
'use client';
import { deletePost } from './actions';
export default function DeleteButton({ postId }) {
return <button onClick={() => deletePost(postId)}>Delete</button>;
}
Real-world example
A blog admin panel lets an editor delete a post with a single button click, calling a server action directly rather than needing to submit a form for such a simple action.
Common follow-ups: Does calling a server action from a button require any special setup compared to a form?;How do you show a loading state while a button triggered server action runs?
Client Components & Hydration;Loading UI & Streaming with Suspense
How do server actions automatically update the user interface after making a change to data?
Intermediate
After a server action completes its update, you typically call revalidatePath or revalidateTag inside it to clear the relevant cached data, which causes Next.js to refetch and re-render the affected parts of the page with the newly updated data the next time they are displayed.
'use server';
import { revalidatePath } from 'next/cache';
export async function markComplete(todoId) {
await db.todo.update({ where: { id: todoId }, data: { completed: true } });
revalidatePath('/todos');
}
Real-world example
A todo list automatically shows a task as completed right after a user checks it off, thanks to the server action revalidating the todos page immediately after updating the database.
Common follow-ups: What happens if you forget to call revalidatePath after a data mutation?;Can a single server action revalidate multiple different pages?
On-Demand Revalidation Strategies;Data Fetching
How would you handle errors gracefully inside a server action and display a meaningful message to the user?
Advanced
You wrap the risky part of your server action in a try and catch block, and instead of letting an unhandled error crash the request, you return a structured object describing what went wrong, which the calling client component can then read, often through the useActionState hook, to show an appropriate error message.
'use server';
export async function createAccount(prevState, formData) {
try {
await db.user.create({ data: { email: formData.get('email') } });
return { success: true };
} catch (error) {
return { success: false, message: 'This email is already registered.' };
}
}
Real-world example
A signup form shows a clear message telling the user their email is already registered, instead of a generic broken error page, because the server action caught the database error and returned a friendly message.
Common follow-ups: Should sensitive error details ever be shown directly to the user?;How do you log errors from server actions for debugging while still showing a friendly message?
Error Handling & Not Found Pages;Form Handling & Validation in Next.js
What security considerations are unique to server actions compared to regular server side code, since they can be called directly from the browser?
Advanced
Since server actions are exposed as callable endpoints, you must treat every input they receive as untrusted, just as you would with a public API, always validating the data's shape, checking that the current user is authorized to perform the action, and never assuming the client sent exactly what your form intended.
'use server';
export async function updateProfile(formData) {
const session = await getSession();
if (!session) throw new Error('Unauthorized');
// Always validate input even though it came from your own form
const name = String(formData.get('name')).slice(0, 100);
await db.user.update({ where: { id: session.user.id }, data: { name } });
}
Real-world example
A profile update feature checks that the request comes from an authenticated user and limits the length of the submitted name, protecting against both unauthorized access and unexpectedly large or malicious input.
Common follow-ups: How is securing a server action similar to securing a public API route?;What tools can help audit server actions for missing authorization checks?
Security Best Practices in Next.js;Authentication & Authorization in Next.js