6. Server Actions

Ayush RajputAyush Rajput
4 min read

Server Actions are functions that run only on the server but can be invoked directly from the client (like a form submission or button click).

They allow you to:

  • Mutate data securely (e.g., insert/update/delete in a DB)

  • Trigger backend logic from the client

  • Keep your logic colocated with the UI

  • Avoid API route boilerplate

  1. Fetch Data in Server Component using server action

//src/actions/index.js
'use server'
export default async function fetchList(){
    const response = await fetch('https://dummyjson.com/products');
    const result = await response.json();
    return result?.products
}

// Now we use this API in any server component or client component
//1 . In server componennt : src/app/server-page-example
import fetchList from "@/actions";  // import that 
export default async function serverActionsExample(){
  const products = await fetchList(); // call that function
  console.log(products)
    return (
        <div>
             <h1 className="text-center">
                Server-Actions Example LOL -- Sever Components 
                <ul>
                    <li>
                        {
                            products?.map((item,i)=>(
                                <p key={i}>
                                    {item.title}
                                </p>
                            ))
                        }
                    </li>
                </ul>
             </h1> 
        </div>
    )
}
  1. Server Actions in client Component

    It is not recommended to use server actions in client component but when we are handle formdata that we use this in client component.

    Ex: Lets suppose you use form in client component and store data of form in use state , then you simply call server (POST) method to do this.

     // in client component
     'use client'
    
     import fetchList from "@/actions"  // import that 
     import { useEffect, useState } from "react"
     export default function clientPageServerAction(){
    
         const[products , setProducts] = useState()
    
         async function getListOfProducts(){
             const data = await fetchList()
             console.log(data)
             setProducts(data)
         }
         useEffect(()=>{
             getListOfProducts()
         },[])
         return (
             <div>
                 <h1 className="text-center">Server-Actions in Client Components </h1>
                 <ul>
                     {
                         products?.map((item,i)=>(
                             <li key={i}>
                                 {item.title}
                             </li>
                         ))
                     }
                 </ul>
             </div>
         )
     }
    
  2. User-management App using Server Actions

    1. Do basic setup of NextApp - Design Home page with ShadCn , Connect Database , Create Models ,Create routes

    2. Create actions folder in src and define actions in it

"use server";

import dbConnect from "@/database";
import User from "@/models/User";

//add the user -> now we call this from client side componnet that have form data 
//No need for HTTP API: You don’t need to create a separate API endpoint—actions are called directly, and data is sent via internal Next.js mechanisms.
export async function addNewUser(formData , pathToReValidate) {
    await dbConnect();
    try{
        const newlyCreatedUser = await User.create(formData);
//DO Validations Here:
        if(newlyCreatedUser){
           revalidatePath(pathToReValidate) // refresh that route/page after adding it
            return {
                success: true,
                message: "User added successfully",
            };
        }
        else{
            return {
                success: false,
                message: "Failed to add user"
            };
        }

    }
    catch(err){
        console.log(err);
        return {
            success: false,
            message: "Internal Server Error"
        };
    }
}
  1. Now this action is for form submission from client component

     // This function just used to call server action when <form action={handleAddNewUserAction}> is submitted
     // Inside it the react state like setState(),setLoading not work, so we have to use the form data directly
         const handleAddNewUserAction = async(e)=>{
             if(Object.keys(addNewUserFormData).some(key=>addNewUserFormData[key].trim()==='')){
                 alert("all fileds are required")
                 return;
             }
             const response = await addNewUser(addNewUserFormData , '/home') //// pass that route that needs to refresh/reValidate when adding new user
             console.log(response)
             if(response.success){
                 alert(response.message)
                 setOpenDialog(false)
                 setAddNewUserFormData({
                     firstName:"",
                     lastName:"",
                     email:"",
                     password:"",
                 })
             }
             else{
                 alert(response.message)
             }
         }
    
    1. Fetch usersList

       // fetch user actions in actions folder
       export async function fetchUsersActions(){
           await dbConnect();
           try{
               const listOfUsers = await User.find({});
               return {
                   success:true,
                   message:"User fetched successfully",
                   data: JSON.parse(JSON.stringify(listOfUsers)) //  Converts mongoose document into a plain JavaScript object otherwise it throw error
               }
      
           }
           catch(err){
               console.log(err)
               return{
                   success:false,
                   message:"Internal Server Error"
               }
           }
       }
      
  1. fetch from frontend

     // fetch server action to get list of user
       const getListOfUsers = await fetchUsersActions();
       console.log(getListOfUsers)
    
  2. Delete User

     export async function deleteUserAction(currentUserId , pathToRevalidate){
         await dbConnect()
         revalidatePath(pathToRevalidate)
         try{
             const deletedUSer =await User.findByIdAndDelete(currentUserId);
             if(deletedUSer){
                 return {
                     success:true,
                     message:"User Deleted Successfully",
                 }
             }
             else{
                 return {
                     success:false,
                     message:"Unable to delete user"
                 }
             }
         }
         catch(err){
             console.log(err)
             return {
                 success:false,
                 message:"Internal Server Error"
             }
         }
     }
    
     // from client component onclick on button
         const handleDeleteUser = async (id)=>{
             const response = await deleteUserAction(id , '/');
             console.log(response)
         }
    
  3. Edit/Update user

    To Edit the user we make a context to store user info to edit

     //src/context/index.js
     'use client'
     import { createContext, useState } from "react";
     export const UserContext = createContext(null);
     export default function UserState({children}){
         const [currentUserId, setCurrentUserId] = useState(null);
         const [openDialog, setOpenDialog] = useState(false);
         const [addNewUserFormData, setAddNewUserFormData] = useState({
             firstName:"",
             lastName:"",
             email:"",
             password:"",
         })
         const value ={
             currentUserId,
             setCurrentUserId,
             openDialog,
             setOpenDialog,
             addNewUserFormData,
             setAddNewUserFormData
         }
         return (
             <UserContext.Provider value={value}>
                 {children}
             </UserContext.Provider>
         )
     }
     // Now create new component src/component/common-layout/index.js
     'use client'
     import UserState from "@/context";
     export default function CommonLayout({children}){
         return <UserState>{children}</UserState>
     }
    
     // in layout.js
         <html lang="en">
           <body
             className={`${geistSans.variable} ${geistMono.variable} antialiased`}  >
             <CommonLayout>{children}</CommonLayout>
           </body>
         </html>
    

    update the user -: on click edit

     export async function editUserAction(currentUserId,formData, pathToReValidate){
         await dbConnect();
         try{
             revalidatePath(pathToReValidate)
             const {firstName , lastName , email , password} = formData;
             const editedUser = await User.findOneAndUpdate({
                 _id:currentUserId,
             }, {firstName,lastName , email , password}, {new:true});
    
             return {
                 success:true,
                 message:"User Updated Successfully",
                 editedUser: JSON.parse(JSON.stringify(editedUser))
             }
         }
         catch(err){
             return {
                 success:false,
                 message:"Internal Server Error"
             }
         }
     }
     // form frontend
         const handleAddNewUserAction = async(e)=>{
             if(Object.keys(addNewUserFormData).some(key=>addNewUserFormData[key].trim()==='')){
                 alert("all fileds are required")
                 return;
             }
             const response = currentUserId !==null? await editUserAction(currentUserId, addNewUserFormData , '/') : await addNewUser(addNewUserFormData , '/')// pass that route that needs to refresh/reValidate when adding new user
             console.log(response)
             alert(response.message)
             setOpenDialog(false)
             setAddNewUserFormData({
                 firstName:"",
                 lastName:"",
                 email:"",
                 password:"",
             })
             setCurrentUserId(null)
         }
    
0
Subscribe to my newsletter

Read articles from Ayush Rajput directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ayush Rajput
Ayush Rajput