#nextjs #server-actions #forms #mutation

Day 15 β€” Server Actions I: Forms without APIs

🧭 Day 15 β€” Server Actions I: Forms without APIs

Zero to Hero β€” Hands-on Next.js Tutorial

Welcome to Week 3! We focused on Getting Data last week. Now we focus on Changing Data (Mutations).

Historically, submitting a form meant:

  1. Create api/submit/route.ts.
  2. Create a Client Component form.
  3. onSubmit, prevent default.
  4. fetch('/api/submit', { method: 'POST' ... }).
  5. Handle loading, error, success state.

Server Actions kill this boilerplate. You can just call a function.


🟦 1. What is a Server Action?

It is an asynchronous function that runs on the server, but can be invoked from the Client or Server. You mark it with the 'use server' directive.

// src/actions.ts
'use server';

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  await db.post.create({ data: { title } });
  console.log("Post created!");
}

🟩 2. Using it in a Form

You can pass this function directly to the action prop of a form. No onSubmit handler needed!

// src/app/page.tsx
import { createPost } from '@/actions';

export default function Page() {
  return (
    <form action={createPost}>
      <input name="title" type="text" className="border p-2" />
      <button type="submit">Create Post</button>
    </form>
  );
}

What happens?

  1. User clicks submit.
  2. Next.js makes a POST request to the same URL.
  3. The createPost function runs on the server.
  4. The page refreshes automatically!

🟧 3. Progressive Enhancement

Because this is a standard HTML <form>, it works without JavaScript. If the user’s JS hasn’t loaded yet, or if they have JS disabled, the form still works. Try doing that with axios and useState!


πŸŸ₯ 4. Revalidation

After we create a post, we want the list of posts to update. We don’t need to manually refetch. We just tell Next.js to purge the cache.

'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

export async function createPost(formData: FormData) {
  await db.post.create({ ... });
  
  // 1. Purge the cache for the home page
  revalidatePath('/');
  
  // 2. Redirect the user
  redirect('/dashboard');
}

πŸ§ͺ Challenge: Day 15

  1. Create a file actions.ts.
  2. Export a function logMessage that takes FormData, gets a β€œmsg” field, and logs it to the server console.
  3. Create a form in page.tsx.
  4. Import logMessage and assign it to <form action={...}>.
  5. Submit the form. Check your terminal (not browser console). You should see the log!

See you tomorrow for Server Actions Part II: Loading & Error States! ⏳