60Developer Portfolio in 60
Back to Maya Rodriguez

Shipping a real feature with only server actions

July 18, 2026

One month rewriting our booking flow to lean entirely on server actions — what stayed, what broke, and the four rules I would keep for next time.

What I actually changed

We took our booking flow — a five-step wizard with three separate mutation endpoints — and moved every write onto a server action. That means no more /api/booking/create, no more client-side POST helpers, no more zod-parsing-on-the-edge. Just use server at the top of the file and <form action={submit}>.

The good

Half the code disappeared. Optimistic UI got easier because useActionState gives you exactly the two things you want: the last returned value and the pending flag. And typed error returns beat throwing.

The bad

Streaming validation is harder. Server actions want a full round trip. For inline field validation we still hit a tiny /api route with a debounce.

Four rules

  1. One action per form. Do not compose actions inside actions — you will regret it the first time you need to redirect.
  2. Return, don't throw. { ok: false, error: '...' } is the shape.
  3. Revalidate paths at the boundary. Never inside the action helper.
  4. Move the schema next to the action. Reuse it in the client only for optimistic guards.