Server Actions

Last updated on 2026-08-30

Every mutation in the kit uses Next.js Server Actions. They run server-side, use the Supabase admin client for writes, and call revalidatePath() to refresh page data.

Architecture

Client Component → Server Action → Supabase Admin Client → Database
                                 → revalidatePath() → Page Refresh

All Server Actions are in lib/actions/ and marked with "use server" at the top of each file.

Product Actions (lib/actions/products.ts)

Action Parameters Description
createProduct FormData Insert product with category, tags, specs
updateProduct id, FormData Update product details
deleteProduct id Delete product and associated images/variants
uploadProductImage productId, file Upload to Supabase Storage

Order Actions (lib/actions/orders.ts)

Action Parameters Description
updateOrderStatus orderId, status Change order status
addOrderNote orderId, message Add a note to the order
updateTrackingNumber orderId, trackingNumber Set shipping tracking

Review Actions (lib/actions/reviews.ts)

Action Parameters Description
respondToReview reviewId, response Add admin response
moderateReview reviewId, status Approve, reject, or flag

Address Actions (lib/actions/addresses.ts)

Action Parameters Description
createAddress FormData Add new address
updateAddress id, FormData Edit existing address
deleteAddress id Remove address
setDefaultAddress id Mark as default

Discount Actions (lib/actions/discounts.ts)

Action Parameters Description
createDiscount FormData Create new discount code
updateDiscount id, FormData Edit discount details
disableDiscount id Set status to disabled

Return Actions (lib/actions/returns.ts)

Action Parameters Description
createReturnRequest orderId, items, reason Submit return request
updateReturnStatus returnId, status Process return

Customer Actions (lib/actions/customers.ts)

Action Parameters Description
getAllCustomers - Fetch all users with order stats (admin)

Error Handling Pattern

All Server Actions follow this pattern:

"use server"

import { createAdminClient } from "@/lib/supabase/admin"
import { revalidatePath } from "next/cache"

export async function updateOrderStatus(orderId: string, status: string) {
  try {
    const supabase = createAdminClient()

    const { error } = await supabase
      .from("orders")
      .update({ status })
      .eq("id", orderId)

    if (error) throw error

    revalidatePath(`/admin/orders/${orderId}`)
    revalidatePath("/admin/orders")

    return { success: true }
  } catch (err) {
    console.error("Failed to update order status:", err)
    return { success: false, error: "Failed to update order status" }
  }
}

Using Server Actions in Components

Server Actions are called from client components using form actions or event handlers:

"use client"

import { updateOrderStatus } from "@/lib/actions/orders"

function OrderActions({ orderId }: { orderId: string }) {
  const [isPending, startTransition] = useTransition()

  function handleStatusChange(status: string) {
    startTransition(async () => {
      const result = await updateOrderStatus(orderId, status)
      if (result.success) {
        toast.success("Order status updated")
      } else {
        toast.error(result.error)
      }
    })
  }

  return (
    <Select onValueChange={handleStatusChange} disabled={isPending}>
      {/* ... */}
    </Select>
  )
}

Supabase Client Selection

Context Client RLS
Public reads (storefront) Server client Yes (anon key)
User reads (account) Server client Yes (user's session)
Admin reads Server client or Admin Admin client bypasses RLS
All writes Admin client Bypassed (service role)
Webhook handler Admin client Bypassed (service role)

The admin client (lib/supabase/admin.ts) uses SUPABASE_SERVICE_ROLE_KEY and should never be exposed to the client.