Logo
KangTLee's blog

เลิกใช้ Next.js Api Route แล้วหันมาใช้ Server Action กัน

Kang T Lee
อัพเดทล่าสุดวันที่ 24 สิงหาคม 2567
เลิกใช้ Next.js Api Route แล้วหันมาใช้ Server Action กัน

ทำไมเราควรจะเลิกใข้ Next.js Api Route และหันมาใช้ Next.js Server Action แทน วันนี้ผมอยากจะมาอธิบายเหตุผล...

หากใครใช้ Next.js ในการสร้าง Fullstack Application จะพบว่าเราสามารถใช้ Api route แทนที่การสร้าง Backend Server ด้วย Node ทำให้เราสามารถติดต่อกับ Database หรือ Api Service อื่นๆ ได้อย่างปลอดภัย และไม่ต้องกังวลว่า Api Key ที่เราใช้จะถูกเปิดเผยให้คนเห็น โดยตัวอย่างการสร้าง Api Route มีดังเช่น

// /api/data/route.ts
export async function POST(req: NextRequest) {
   const body = await req.json();
   const data = await db.create(body);
   return NextResponse.json({ data }, { status: 200 });
}

จากตัวอย่าง เราจะทำการสร้าง Api Endpoint เป็น Http Post Request ที่ /api/data เพื่อทำการสร้างข้อมูลและบันทึกลงใน Database โดยเราจะรับข้อมูล body ด้วย req.json(); และตัว Api endpoint จะส่งข้อมูลที่ถูกสร้างใน Database กลับมาเป็น data ด้วย NextResponse.json({ data }, { status: 200 })

และในฝั่ง Front-end เราสามารถดึงข้อมูลด้วยคำสั่ง fetch เช่น

const resp = await fetch("/api/data", {
      method: "POST",
      body: JSON.stringify(body),
    });
const data = await resp.json();    

ซึ่งจะเป็นการส่งข้อมูล body ไปยัง /api/data และเราจะได้ data กลับมา

จากตัวอย่างนี้ เราจะเห็นข้อเสียหลักๆอยู่สองข้อ คือ

  1. หากเราเปลี่ยน path ของ Api Endpoint เราต้องกลับมาแก้ url param ในคำสั่ง fetch ทุกครั้ง

  2. ไม่มี Type safe ในการส่งข้อมูล body และในการรับข้อมูล data

ข้อเสียแรก เช่น หากเราต้องการเปลี่ยนจาก /api/data ไปเป็น /api/v1/data เราจะต้องแก้ url path ในทุกๆฟังก์ชั่นที่เราเรียกใช้ fetch ให้เป็น fetch("/api/v1/data", ....)

และข้อเสียที่สอง การไม่มี Type safe หมายความว่าเราไม่สามารถใช้ Typescript ในการช่วยตรวจสอบความถูกต้องของ data type ในขณะที่เรากำลัง develope แอพได้ หากเราต้องการเปลี่ยนประเภทของ Body เช่น

//ก่อน
interface BodyProps {
  title: string;
  content: string;
}
//หลัง
interface BodyProps {
  title: string;
  content: string;
  imgUrl: string;
}

หากเราลืมแก้ข้อมูลทั้งในฟังก์ชั่น fetch และใน api route เราก็จะเจอ Error เมื่อเราทำการเรียกใช้ฟังก์ชั่นดังกล่าว

ปัญหาดังกล่าว อาจจะแก้ได้ไม่ยากถ้าเรากำลัง Develope แอพเพียงคนเดียว และขนาดของโปรเจ็คไม่ได้มีขนาดใหญ่มากนัก แต่หากโปรเจ็คของเรามีขนาดใหญ่ และเราต้องทำงานร่วมกับทีม การเปลี่ยนประเภทของ Body หรือ Data จะยุ่งยาก และทำให้เกิด Error ได้ง่าย หากเราไม่มีการสื่อสารระหว่างทีมที่ดี และ Typescript จะไม่ช่วยในการตรวจจับความผิดพลาด

Server Action save the day

หลังจาก Next.js เวอร์ชั่น 14 เป็นตันไป เราสามารถใช้ Server action ในการเรียกใช้คำสั่งแทนที่การใช้ Api Route ได้ด้วยวิธีง่ายๆดังนี้

สร้างไฟล์ที่เราต้องการใช้ในการติดต่อกับ Database โดยมีคำว่า "use server" ข้างบน เป็น

"use server";
export const createNewData = async (body:BodyProps)=>{
    const data = await db.create(body);
    return data
}

และเรียกใช้งาน server-action ได้ในฝั่ง Client เช่นเมื่อเรากดปุ่ม Button ด้วยคำสั่ง

<button onClick={handleOnClick}>
   สร้างข้อมูล
</button>

และ

const handleOnClick = async ()=>{
   const data = await createNewData(body)
   setData(data)
}

เราจะทำการส่งข้อมูล body ไปยัง Database เพื่อสร้างข้อมูล Data

หากเทียบกับการใช้ Next Api Route เราจะเห็นว่าการเรียกใช้ฟังก์ชั่น createNewData โดยตรง จะทำให้ Typescript สามารถตรวจสอบความถูกต้องของ body ที่เป็น input และ data ที่เป็น output ได้ เราจึงไม่ต้องกัลวลว่าเมื่อเราเปลี่ยน Data type ของ body หรือ output data แล้วเราจะลืมแก้ไขประเภทของข้อมูล เพราะ Typescript จะฟ้องเมื่อเราป้อนข้อมูลผิดประเภท

ข้อควรระวังในการใช้ Server action

การใช้ Server action ก็เหมือนการสร้าง Api Route endpoint ทั่วไป เมื่อเราทำการเรียกใช้ Server action ในหน้าหน้าหนึ่ง เช่นใน /signup/page.tsx หากเราสังเกตใน server log เราจะเห็นว่าตัว Server จะทำการเสริฟ POST response ในหน้า /signup นั่นหมายความว่า หากเราส่ง http request ที่เป็น POST ไปยัง url ที่เป็น http://baseurl.com/signup จะทำให้เราสามารถเข้าถึงตัวฟังก์ชั่นของ server action ได้ หากเราไม่มี logic ที่จะตรวจสอบความถูกต้องใน server action เราก็อาจเปิดเผยข้อมูลบางอย่างที่เราไม่ต้องการจะเปิดเผยได้

ดังนั้น เหมือนดังเช่นการออกแบบ Api Route เราจะต้องมีการตรวจสอบ (Validate) ความถูกต้องของ input และหากเป็น function ที่เราต้องการให้มีการเข้ารหัส เราก็ต้องตรวจสอบด้วยการทำ Authorization เหมือนที่เราออกแบบ Api Route นั่นเอง ตัวอย่างเช่น

"use server";
export const createNewData = async (body:BodyProps, sessionId:string)=>{
    if(!body) return { error: "body not found" } //ตรวจสอบ body
    const session = await verifySession(sessionId);
    if (!session) return { error: "unauthorized" } //ตรวจสอบว่า user ถูกต้อง
    const data = await db.create(body);
    return data
}

จากตัวอย่าง เราจะเพิ่มการตรวจสอบ body และการตรวจสอบ session เพื่อมั่นใจว่าผู้ที่ต้องการจะติดต่อกับ Database จะต้องทำการล็อคอินเข้าสู่ระบบ และป้อนข้อมูล body ที่ถูกต้อง

เมื่อไหร่ที่เราควรใช้ Server action

ข้อดีของ Server action ใน Next.js คือหากเราสร้าง Server action เก็บไว้ในโฟลเดอร์ เราสามารถเรียกใช้ฟังก์ชั่นดังกล่าวได้ทุกที่ ไม่ว่าจะเป็นในฝั่ง client side หรือจะเป็น server side ก็ตาม หากเราเรียกใช้ฟังก์ชั่นในฝั่ง client side เราจะทำการเรียกใช้ Server action หรือ POST request ไปยังหน้าที่เรากำลังเรียกใช้ฟังก์ชั่น และหากเราเรียกใช้ฟังก์ชั่นในฝั่ง server side เช่นใน React Server Component ก็จะเป็นแค่การเรียกใช้ฟังก์ชั่นปกติใน Server side นั่นเอง

ดังนั้น เราสามารถใช้ Server action เมื่อ

  1. เราต้องการแก้ไข หรือสร้างข้อมูลชุดใหม่ด้วยและเก็บไว้ใน Database โดยเราจะใช้ใน client side

  2. เราต้องการดึงข้อมูล หลายๆครั้งเมื่อมี event เกิดขึ้น เช่นการกดปุ่ม ตัวอย่างเช่น หากเราต้องการดึงข้อมูลจำนวนขนาดมาก ขนาด 1,000 ข้อมูล เราสามารถแบ่งข้อมูลเป็นหน้า หน้าละ 100 ชุดข้อมูล และเมื่อกดปุ่ม เราจะทำการเรียก server action เพื่อดึงข้อมูลทีละ 100 ชุด ซึ่งการแบ่งข้อมูลดังกล่าวเราจะเรียกว่า Pagination

  3. เราต้องการดึงข้อมูลใน Server component เช่นการตรวจสอบ Authorization ของ User ซึ่งเมื่อเราทำการเรียก Server action นั้น สิ่งที่เกิดขึ้นคือการเรียกใช้ฟังก์ชั่นทั่วไป ไม่ใช่การเรียกใช้ Server action เพิ่ม นั่นหมายความว่าเราไม่จำเป็นตัวเขียน function ซ้ำ เพราะเราสามารถใช้ได้ทั้งใน Client side และ Server side นั่นเอง

  4. ใช้ Server action เพื่อตรวจ cookies โดยใช้ headers จาก next library ซึ่งทำให้สามารถตรวจสอบ Session Id ที่เก็บไว้ใน cookie ได้

สรุป

เมื่อไหร่ก็ตามที่เราต้องการจะติดต่อกับ Database หรือทำการใช้ Api โดยมี Api key ที่เราไม่ต้องการเปิดเผยในฝั่ง client side เราสามารถใช้ Server action แทนการสร้าง Api Route ได้ เพราะ Server action สามารถทำทุกอย่างได้เทียบเท่ากับการใช้ Api route เลย เพียงแค่เราต้องตรวจสอบความถูกต้องของ input และเราต้องทำ authorization ซึ่งจะไม่ต่างจากการออกแบบ Api Route ใน Next.Js นั่นเอง

หากใครมีข้อสงสัย หรือต้องการจะพูดคุย สามารถคอมเมนท์มาได้ หรือจะส่งข้อความหาผมได้ที่นี่

Kang T Lee

Kang T Lee

ผม Kang T Lee ผมเขียนบทความเกี่ยวกับ Web development, Lifestyle

Tags:

บทความที่เกี่ยวข้อง