ไทย

คู่มือฉบับสมบูรณ์เกี่ยวกับ Next.js 14 Server Actions ครอบคลุมแนวทางปฏิบัติที่ดีที่สุดในการจัดการฟอร์ม การตรวจสอบข้อมูล ข้อควรพิจารณาด้านความปลอดภัย และเทคนิคขั้นสูงสำหรับการสร้างเว็บแอปพลิเคชันสมัยใหม่

Next.js 14 Server Actions: แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการฟอร์มอย่างมืออาชีพ

Next.js 14 มาพร้อมกับฟีเจอร์อันทรงพลังสำหรับการสร้างเว็บแอปพลิเคชันที่มีประสิทธิภาพและใช้งานง่าย หนึ่งในนั้นคือ Server Actions ซึ่งโดดเด่นในฐานะวิธีการปฏิวัติการจัดการการส่งฟอร์มและการเปลี่ยนแปลงข้อมูลโดยตรงบนเซิร์ฟเวอร์ คู่มือนี้จะให้ภาพรวมที่ครอบคลุมเกี่ยวกับ Server Actions ใน Next.js 14 โดยเน้นที่แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการฟอร์ม การตรวจสอบข้อมูล ความปลอดภัย และเทคนิคขั้นสูง เราจะสำรวจตัวอย่างที่ใช้งานได้จริงและให้ข้อมูลเชิงลึกที่นำไปปฏิบัติได้เพื่อช่วยให้คุณสร้างเว็บแอปพลิเคชันที่แข็งแกร่งและขยายขนาดได้

Next.js Server Actions คืออะไร?

Server Actions คือฟังก์ชันแบบอะซิงโครนัสที่ทำงานบนเซิร์ฟเวอร์และสามารถเรียกใช้ได้โดยตรงจากคอมโพเนนต์ของ React มันช่วยลดความจำเป็นในการสร้าง API routes แบบดั้งเดิมสำหรับการจัดการการส่งฟอร์มและการเปลี่ยนแปลงข้อมูล ส่งผลให้โค้ดเรียบง่ายขึ้น ความปลอดภัยดีขึ้น และประสิทธิภาพสูงขึ้น Server Actions เป็น React Server Components (RSCs) ซึ่งหมายความว่ามันจะถูกประมวลผลบนเซิร์ฟเวอร์ ทำให้การโหลดหน้าเว็บครั้งแรกเร็วขึ้นและดีต่อ SEO

ประโยชน์หลักของ Server Actions:

การตั้งค่าโปรเจกต์ Next.js 14 ของคุณ

ก่อนที่จะลงลึกในเรื่อง Server Actions ตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าโปรเจกต์ Next.js 14 เรียบร้อยแล้ว หากคุณเริ่มต้นจากศูนย์ ให้สร้างโปรเจกต์ใหม่โดยใช้คำสั่งต่อไปนี้:

npx create-next-app@latest my-next-app

ตรวจสอบให้แน่ใจว่าโปรเจกต์ของคุณใช้โครงสร้างไดเรกทอรี app เพื่อใช้ประโยชน์จาก Server Components และ Actions ได้อย่างเต็มที่

การจัดการฟอร์มเบื้องต้นด้วย Server Actions

เรามาเริ่มด้วยตัวอย่างง่ายๆ: ฟอร์มที่ส่งข้อมูลเพื่อสร้างรายการใหม่ในฐานข้อมูล เราจะใช้ฟอร์มง่ายๆ ที่มีช่องป้อนข้อมูลและปุ่มส่ง

ตัวอย่าง: การสร้างรายการใหม่

ขั้นแรก กำหนดฟังก์ชัน Server Action ภายในคอมโพเนนต์ React ของคุณ ฟังก์ชันนี้จะจัดการตรรกะการส่งฟอร์มบนเซิร์ฟเวอร์

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // จำลองการทำงานกับฐานข้อมูล
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // จำลองความหน่วงแฝง

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    await createItem(formData);
    setIsSubmitting(false);
  }

  return (
    
); }

คำอธิบาย:

การตรวจสอบข้อมูล (Data Validation)

การตรวจสอบข้อมูลเป็นสิ่งสำคัญอย่างยิ่งในการรับรองความสมบูรณ์ของข้อมูลและป้องกันช่องโหว่ด้านความปลอดภัย Server Actions เป็นโอกาสที่ดีเยี่ยมในการตรวจสอบข้อมูลฝั่งเซิร์ฟเวอร์ แนวทางนี้ช่วยลดความเสี่ยงที่เกี่ยวข้องกับการตรวจสอบฝั่งไคลเอ็นต์เพียงอย่างเดียว

ตัวอย่าง: การตรวจสอบข้อมูลอินพุต

แก้ไข Server Action createItem เพื่อรวมตรรกะการตรวจสอบข้อมูลเข้าไป

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  if (!name || name.length < 3) {
    throw new Error('ชื่อรายการต้องมีความยาวอย่างน้อย 3 ตัวอักษร');
  }

  // จำลองการทำงานกับฐานข้อมูล
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // จำลองความหน่วงแฝง

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'เกิดข้อผิดพลาด');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

คำอธิบาย:

การใช้ไลบรารีสำหรับการตรวจสอบข้อมูล

สำหรับสถานการณ์การตรวจสอบข้อมูลที่ซับซ้อนยิ่งขึ้น ลองพิจารณาใช้ไลบรารีสำหรับการตรวจสอบข้อมูล เช่น:

นี่คือตัวอย่างการใช้ Zod:

// app/utils/validation.ts
import { z } from 'zod';

export const CreateItemSchema = z.object({
  name: z.string().min(3, 'ชื่อรายการต้องมีความยาวอย่างน้อย 3 ตัวอักษร'),
});
// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { CreateItemSchema } from '../utils/validation';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  const validatedFields = CreateItemSchema.safeParse({ name });

  if (!validatedFields.success) {
    return { errors: validatedFields.error.flatten().fieldErrors };
  }

  // จำลองการทำงานกับฐานข้อมูล
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // จำลองความหน่วงแฝง

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'เกิดข้อผิดพลาด');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

คำอธิบาย:

ข้อควรพิจารณาด้านความปลอดภัย

Server Actions ช่วยเพิ่มความปลอดภัยโดยการรันโค้ดบนเซิร์ฟเวอร์ แต่ก็ยังคงเป็นสิ่งสำคัญที่จะต้องปฏิบัติตามแนวทางปฏิบัติด้านความปลอดภัยที่ดีที่สุดเพื่อปกป้องแอปพลิเคชันของคุณจากภัยคุกคามทั่วไป

การป้องกัน Cross-Site Request Forgery (CSRF)

การโจมตีแบบ CSRF ใช้ประโยชน์จากความไว้วางใจที่เว็บไซต์มีต่อเบราว์เซอร์ของผู้ใช้ เพื่อป้องกันการโจมตีแบบ CSRF ให้ใช้กลไกการป้องกัน CSRF

Next.js จะจัดการการป้องกัน CSRF โดยอัตโนมัติเมื่อใช้ Server Actions เฟรมเวิร์กจะสร้างและตรวจสอบโทเค็น CSRF สำหรับการส่งฟอร์มแต่ละครั้ง เพื่อให้แน่ใจว่าคำขอมาจากแอปพลิเคชันของคุณ

การจัดการการยืนยันตัวตนและการให้สิทธิ์ผู้ใช้

ตรวจสอบให้แน่ใจว่ามีเพียงผู้ใช้ที่ได้รับอนุญาตเท่านั้นที่สามารถดำเนินการบางอย่างได้ ใช้กลไกการยืนยันตัวตนและการให้สิทธิ์เพื่อปกป้องข้อมูลและฟังก์ชันที่ละเอียดอ่อน

นี่คือตัวอย่างการใช้ NextAuth.js เพื่อป้องกัน Server Action:

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { getServerSession } from 'next-auth';
import { authOptions } from '../../app/api/auth/[...nextauth]/route';

async function createItem(formData: FormData) {
  'use server'

  const session = await getServerSession(authOptions);

  if (!session) {
    throw new Error('ไม่ได้รับอนุญาต');
  }

  const name = formData.get('name') as string;

  // จำลองการทำงานกับฐานข้อมูล
  console.log('Creating item:', name, 'by user:', session.user?.email);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // จำลองความหน่วงแฝง

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'เกิดข้อผิดพลาด');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

คำอธิบาย:

การกรองข้อมูลอินพุต (Sanitizing Input Data)

กรองข้อมูลอินพุตเพื่อป้องกันการโจมตีแบบ Cross-Site Scripting (XSS) การโจมตีแบบ XSS เกิดขึ้นเมื่อโค้ดที่เป็นอันตรายถูกฉีดเข้าไปในเว็บไซต์ ซึ่งอาจเป็นอันตรายต่อข้อมูลผู้ใช้หรือการทำงานของแอปพลิเคชัน

ใช้ไลบรารีเช่น DOMPurify หรือ sanitize-html เพื่อกรองข้อมูลที่ผู้ใช้ป้อนเข้ามาก่อนที่จะประมวลผลใน Server Actions ของคุณ

เทคนิคขั้นสูง

เมื่อเราได้ครอบคลุมพื้นฐานแล้ว มาสำรวจเทคนิคขั้นสูงบางอย่างสำหรับการใช้ Server Actions อย่างมีประสิทธิภาพกัน

การอัปเดตเชิงบวก (Optimistic Updates)

การอัปเดตเชิงบวกมอบประสบการณ์ผู้ใช้ที่ดีขึ้นโดยการอัปเดต UI ทันทีเสมือนว่าการกระทำนั้นจะสำเร็จ แม้กระทั่งก่อนที่เซิร์ฟเวอร์จะยืนยันก็ตาม หากการกระทำล้มเหลวบนเซิร์ฟเวอร์ UI จะถูกเปลี่ยนกลับไปสู่สถานะก่อนหน้า

// app/components/UpdateItemForm.tsx
'use client';

import { useState } from 'react';

async function updateItem(id: string, formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // จำลองการทำงานกับฐานข้อมูล
  console.log('Updating item:', id, 'with name:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // จำลองความหน่วงแฝง

  // จำลองความล้มเหลว (เพื่อการสาธิต)
  const shouldFail = Math.random() < 0.5;
  if (shouldFail) {
    throw new Error('ไม่สามารถอัปเดตรายการได้');
  }

  console.log('Item updated successfully!');
  return { name }; // ส่งคืนชื่อที่อัปเดตแล้ว
}

export default function UpdateItemForm({ id, initialName }: { id: string; initialName: string }) {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  const [itemName, setItemName] = useState(initialName);

  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);

    // อัปเดต UI ในเชิงบวก
    const newName = formData.get('name') as string;
    setItemName(newName);

    try {
      const result = await updateItem(id, formData);
      //หากสำเร็จ การอัปเดตจะแสดงผลใน UI ผ่าน setItemName แล้ว

    } catch (error: any) {
      setErrorMessage(error.message || 'เกิดข้อผิดพลาด');
      // ย้อนกลับ UI เมื่อเกิดข้อผิดพลาด
      setItemName(initialName);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    

ชื่อปัจจุบัน: {itemName}

{errorMessage &&

{errorMessage}

}
); }

คำอธิบาย:

การตรวจสอบข้อมูลใหม่ (Revalidating Data)

หลังจากที่ Server Action แก้ไขข้อมูล คุณอาจต้องทำการ revalidate ข้อมูลที่แคชไว้เพื่อให้แน่ใจว่า UI แสดงการเปลี่ยนแปลงล่าสุด Next.js มีหลายวิธีในการ revalidate ข้อมูล:

นี่คือตัวอย่างของการ revalidate path หลังจากสร้างรายการใหม่:

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { revalidatePath } from 'next/cache';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // จำลองการทำงานกับฐานข้อมูล
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // จำลองความหน่วงแฝง

  console.log('Item created successfully!');

  revalidatePath('/items'); // Revalidate path /items
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'เกิดข้อผิดพลาด');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

คำอธิบาย:

แนวทางปฏิบัติที่ดีที่สุดสำหรับ Server Actions

เพื่อเพิ่มประโยชน์สูงสุดจาก Server Actions ให้พิจารณาแนวทางปฏิบัติที่ดีที่สุดต่อไปนี้:

ข้อผิดพลาดที่พบบ่อยและวิธีหลีกเลี่ยง

แม้ว่า Server Actions จะมีข้อดีมากมาย แต่ก็มีข้อผิดพลาดทั่วไปบางประการที่ควรระวัง:

สรุป

Next.js 14 Server Actions เป็นวิธีที่ทรงพลังและมีประสิทธิภาพในการจัดการการส่งฟอร์มและการเปลี่ยนแปลงข้อมูลโดยตรงบนเซิร์ฟเวอร์ โดยการปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดที่ระบุไว้ในคู่มือนี้ คุณสามารถสร้างเว็บแอปพลิเคชันที่แข็งแกร่ง ปลอดภัย และมีประสิทธิภาพได้ ลองใช้ Server Actions เพื่อทำให้โค้ดของคุณง่ายขึ้น เพิ่มความปลอดภัย และปรับปรุงประสบการณ์ผู้ใช้โดยรวม ในขณะที่คุณนำหลักการเหล่านี้ไปใช้ ให้พิจารณาถึงผลกระทบในระดับโลกจากตัวเลือกการพัฒนาของคุณ ตรวจสอบให้แน่ใจว่าฟอร์มและกระบวนการจัดการข้อมูลของคุณสามารถเข้าถึงได้ ปลอดภัย และเป็นมิตรกับผู้ใช้สำหรับผู้ชมจากนานาชาติที่หลากหลาย ความมุ่งมั่นในการสร้างความเท่าเทียมนี้ไม่เพียงแต่จะปรับปรุงการใช้งานของแอปพลิเคชันของคุณ แต่ยังขยายการเข้าถึงและประสิทธิผลในระดับโลกอีกด้วย