Bird
Raised Fist0
NextJSframework~8 mins

Server action database mutations in NextJS - Performance & Optimization

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Performance: Server action database mutations
MEDIUM IMPACT
This affects the time to update data on the server and how quickly the UI reflects those changes, impacting interaction responsiveness and perceived speed.
Updating user data with server actions in Next.js
NextJS
let dbConnection;
export async function updateUser(data) {
  if (!dbConnection) {
    dbConnection = await db.connect();
  }
  await db.users.updateOne({ id: data.id }, { $set: data });
  // Keep connection open for batch mutations
}

// Debounce input to reduce calls
Reusing the database connection and batching updates reduces latency and server load.
📈 Performance GainReduces blocking time to under 50ms per action, improving INP significantly
Updating user data with server actions in Next.js
NextJS
export async function updateUser(data) {
  await db.connect();
  await db.users.updateOne({ id: data.id }, { $set: data });
  await db.disconnect();
}

// Called on every user input change triggering multiple server actions rapidly
Connecting and disconnecting the database on every small mutation causes high latency and blocks UI updates.
📉 Performance CostBlocks rendering for 200-500ms per action, causing input lag and poor INP
Performance Comparison
PatternDOM OperationsReflowsPaint CostVerdict
Connect/disconnect DB per mutationN/AN/ABlocks UI update 200-500ms[X] Bad
Reuse DB connection and batch mutationsN/AN/ABlocks UI update <50ms[OK] Good
Rendering Pipeline
Server actions run on the server and update the database, then the client receives updated data to re-render UI. Slow server actions delay the next paint after user input.
Server Processing
Network
Client Rendering
⚠️ BottleneckServer Processing time for database mutation
Core Web Vital Affected
INP
This affects the time to update data on the server and how quickly the UI reflects those changes, impacting interaction responsiveness and perceived speed.
Optimization Tips
1Reuse database connections in server actions to avoid repeated connection overhead.
2Batch multiple mutations into a single server action call to reduce latency.
3Debounce client calls to server actions to prevent flooding the server with requests.
Performance Quiz - 3 Questions
Test your performance knowledge
What is the main performance issue with connecting and disconnecting the database on every server action call?
AIt causes layout shifts on the client.
BIt causes high latency and blocks UI updates.
CIt increases the bundle size significantly.
DIt improves caching efficiency.
DevTools: Performance
How to check: Record a performance profile while triggering server actions. Look for long tasks on the server and delayed next paint after input.
What to look for: Long blocking times after user input and delayed UI updates indicate slow server actions.

Practice

(1/5)
1. What is the main purpose of using server actions for database mutations in Next.js?
easy
A. To run client-side animations after data changes
B. To securely update data on the server without exposing logic to the client
C. To fetch data from an external API on the client
D. To style components dynamically based on user input

Solution

  1. Step 1: Understand server actions role

    Server actions run on the server and handle data changes securely.
  2. Step 2: Identify the security benefit

    They keep mutation logic hidden from the client, preventing misuse.
  3. Final Answer:

    To securely update data on the server without exposing logic to the client -> Option B
  4. Quick Check:

    Server actions = secure server-side mutations [OK]
Hint: Server actions run on server to keep data safe [OK]
Common Mistakes:
  • Thinking server actions run on the client
  • Confusing server actions with client-side fetching
  • Believing server actions handle UI styling
2. Which of the following is the correct way to declare a server action function in Next.js?
easy
A. export async function addUser() { 'use server'; /* mutation code */ }
B. export async function addUser() { useServer(); /* mutation code */ }
C. export async function addUser() { /* mutation code */ } 'use server';
D. 'use server'; export async function addUser() { /* mutation code */ }

Solution

  1. Step 1: Identify correct 'use server' placement

    The 'use server' directive must be the first statement inside the function file or function scope.
  2. Step 2: Check syntax correctness

    'use server'; export async function addUser() { /* mutation code */ } places 'use server' at the top before the function, which is correct syntax.
  3. Final Answer:

    'use server'; export async function addUser() { /* mutation code */ } -> Option D
  4. Quick Check:

    'use server' directive must be at top [OK]
Hint: Put 'use server' at the top before function export [OK]
Common Mistakes:
  • Placing 'use server' inside function body after code
  • Using a function call like useServer() instead of directive
  • Putting 'use server' after function declaration
3. Given this server action code, what will be the result after calling await addUser({ name: 'Alice' }) if the database is empty?
 'use server';
 async function addUser(user) {
   await db.users.create({ data: user });
   return await db.users.count();
 }
medium
A. Returns 1 because one user is added
B. Returns undefined because no return statement
C. Throws an error because 'use server' is misplaced
D. Returns 0 because count is called before creation

Solution

  1. Step 1: Analyze database mutation

    The function creates a user in the database with given data.
  2. Step 2: Check return value

    After creation, it returns the count of users, which should be 1.
  3. Final Answer:

    Returns 1 because one user is added -> Option A
  4. Quick Check:

    Created one user, count = 1 [OK]
Hint: Create then count means count reflects new data [OK]
Common Mistakes:
  • Assuming count runs before creation completes
  • Confusing 'use server' placement causing error
  • Missing return statement in function
4. Identify the error in this server action code snippet:
'use server';
export async function updateUser(id, data) {
  await db.users.update({ where: { id }, data });
}
medium
A. Missing semicolon after 'use server' directive
B. Missing export keyword
C. Incorrect object structure in update call
D. No error, code is correct

Solution

  1. Step 1: Check 'use server' directive syntax

    The 'use server'; directive is correctly placed at the top of the module.
  2. Step 2: Analyze update method parameters

    The update method expects an object with 'where' and 'data' keys, but here 'data' is outside the object, causing syntax error.
  3. Final Answer:

    Incorrect object structure in update call -> Option C
  4. Quick Check:

    update expects { where: ..., data: ... } object [OK]
Hint: Check object keys carefully in db update calls [OK]
Common Mistakes:
  • Assuming missing semicolon causes error
  • Forgetting to wrap data inside update object
  • Missing export keyword (actually present)
5. You want to create a server action that deletes a user only if they have no active orders. Which approach correctly combines server action and database mutation logic?
 'use server';
 async function deleteUserIfNoOrders(userId) {
   const orders = await db.orders.findMany({ where: { userId, status: 'active' } });
   if (orders.length === 0) {
     await db.users.delete({ where: { id: userId } });
     return 'Deleted';
   }
   return 'Has active orders';
 }
hard
A. This code correctly checks orders before deleting user
B. This code deletes user without checking orders
C. This code will throw error because 'use server' is misplaced
D. This code returns 'Deleted' even if user has active orders

Solution

  1. Step 1: Verify order check logic

    The function queries active orders for the user and checks if none exist.
  2. Step 2: Confirm conditional deletion

    If no active orders, it deletes the user and returns 'Deleted'; otherwise returns 'Has active orders'.
  3. Final Answer:

    This code correctly checks orders before deleting user -> Option A
  4. Quick Check:

    Conditional delete based on orders = correct [OK]
Hint: Check conditions before mutation to avoid unwanted deletes [OK]
Common Mistakes:
  • Deleting user without checking orders first
  • Misplacing 'use server' directive
  • Returning wrong message ignoring condition