ทำไมต้องมีการตรวจสอบข้อมูลที่ส่งเข้ามา (Request Validation)
เวลาเราสร้าง API (ช่องทางสื่อสารระหว่างโปรแกรม) ขึ้นมาสักตัว เรามักจะเปิดรับข้อมูลจากผู้ใช้งานผ่านทาง Request Body (ส่วนของข้อมูลที่ส่งมาพร้อมกับคำขอ) ถ้าเราเชื่อใจข้อมูลเหล่านั้นมากเกินไปโดยไม่ตรวจสอบอะไรเลย โปรแกรมของเราอาจจะพังได้ง่ายๆ เหมือนกับร้านอาหารที่ยอมให้ลูกค้าสั่งเมนูอะไรก็ได้ที่ไม่มีในร้าน ซึ่งจะทำให้พ่อครัวงงและทำอาหารออกมาไม่ได้
การทำ Request Validation (ขั้นตอนการตรวจสอบความถูกต้องของข้อมูล) จึงเปรียบเสมือนพนักงานต้อนรับที่คอยเช็กว่า ข้อมูลที่ส่งมานั้นครบถ้วนและถูกต้องตามรูปแบบที่เราต้องการหรือไม่ เช่น ถ้าเราต้องการให้ผู้ใช้สมัครสมาชิก เราก็ต้องเช็กว่าเขาใส่ชื่อและอีเมลมาครบไหม หรืออีเมลมีรูปแบบที่ถูกต้องหรือเปล่า การป้องกันตั้งแต่ด่านแรกนี้จะช่วยลดภาระของฐานข้อมูลและป้องกันบั๊ก (ข้อผิดพลาดของโปรแกรม) ที่อาจเกิดขึ้นในอนาคต
หัวใจสำคัญ ของการพัฒนาซอฟต์แวร์ที่ดีคือการไม่ไว้ใจข้อมูลจากภายนอก 100% การทำระบบตรวจสอบที่ดีจะช่วยให้เราเขียนโค้ดได้มั่นใจขึ้น เพราะเราสามารถการันตีได้ว่าข้อมูลที่ผ่านด่านตรวจสอบเข้ามานั้นสะอาดและพร้อมใช้งานเสมอ ไม่ต้องไปเขียนโค้ดดักเช็กเงื่อนไขซ้ำซ้อนในทุกๆ ส่วนของโปรแกรมอีกต่อไป
รู้จักกับ Zod เครื่องมือช่วยเช็กข้อมูลให้ง่ายขึ้น
Zod (ไลบรารีสำหรับกำหนดโครงสร้างข้อมูลและตรวจสอบความถูกต้อง) เป็นเครื่องมือที่ได้รับความนิยมมากในปัจจุบัน เพราะมันช่วยให้เราประกาศโครงสร้างข้อมูลได้อย่างชัดเจนและเข้าใจง่ายเหมือนการเขียนภาษาอังกฤษทั่วไป แถมยังทำงานร่วมกับ TypeScript (ภาษาที่เพิ่มระบบตรวจสอบชนิดข้อมูลให้ JavaScript) ได้ดีเยี่ยม ทำให้เราเห็นคำแนะนำขณะเขียนโค้ดได้ทันที
ลองนึกภาพว่าเราต้องการสร้างระบบสมัครสมาชิก เราต้องกำหนดว่าชื่อต้องเป็นข้อความและอีเมลต้องเป็นรูปแบบอีเมลจริง แทนที่จะเขียนโค้ดดักเงื่อนไขยาวเหยียด เราสามารถใช้ Zod Schema (คำจำกัดความของรูปแบบข้อมูล) มากำหนดกฎเกณฑ์ได้เลย ซึ่งช่วยลดความซับซ้อนของโค้ดลงไปได้มหาศาลสำหรับมือใหม่ที่ยังไม่คุ้นเคยกับการเขียนเงื่อนไขที่ซับซ้อน
การใช้เครื่องมืออย่าง Zod ไม่ได้แค่ช่วยให้งานเร็วขึ้น แต่ยังช่วยให้โค้ดของเราอ่านง่ายขึ้นมาก ถ้าวันหนึ่งเราต้องการเปลี่ยนกฎ เช่น เพิ่มจำนวนตัวอักษรขั้นต่ำของชื่อ เราก็แค่แก้ที่ Schema บรรทัดเดียว ทุกอย่างก็จะอัปเดตตามทันทีโดยไม่ต้องไปไล่แก้โค้ดในทุกฟังก์ชันที่เรียกใช้ข้อมูลนั้น
const { z } = require("zod");
// กำหนดกฎว่าต้องมีชื่อและอีเมล
const userSchema = z.object({
name: z.string().min(1, "ชื่อต้องไม่ว่างเปล่า"),
email: z.string().email("รูปแบบอีเมลไม่ถูกต้อง"),
});
// ข้อมูลสมมติที่ส่งเข้ามา
const userData = { name: "Somchai", email: "invalid-email" };
// ตรวจสอบข้อมูล
const result = userSchema.safeParse(userData);
if (!result.success) {
console.log(result.error.format()); // แสดงข้อผิดพลาด
}
ในตัวอย่างนี้ เรานำเข้า z จาก Zod เพื่อสร้างกฎ userSchema ขึ้นมา โดยกำหนดให้ name เป็นข้อความและ email ต้องเป็นอีเมลที่ถูกต้อง จากนั้นเราใช้ safeParse เพื่อเช็กข้อมูล ถ้าข้อมูลไม่ถูกต้อง result.success จะเป็น false และเราจะเห็นรายละเอียดข้อผิดพลาดผ่าน result.error.format()
ผลลัพธ์ที่ได้จากการรันโค้ดข้างต้นคือข้อความแจ้งเตือนข้อผิดพลาดที่ระบุว่าอีเมลไม่ถูกต้องตามรูปแบบ ซึ่งช่วยให้เราสามารถส่งข้อความนี้กลับไปบอกผู้ใช้งานได้ทันทีว่าพวกเขาทำอะไรผิดพลาดไปบ้าง
การทำ Centralized Error Handling Middleware
เมื่อเราตรวจสอบข้อมูลแล้วพบว่าไม่ผ่าน เราต้องส่งคำตอบกลับไปหาผู้ใช้ แต่ถ้าเราเขียนโค้ดส่งข้อความผิดพลาดแยกไปในทุกๆ ฟังก์ชัน จะทำให้โค้ดรกและจัดการยากมาก Centralized Error Handling (การรวมจุดจัดการข้อผิดพลาดไว้ที่เดียว) จึงเป็นแนวทางที่ดีที่สุด เพราะเราสามารถกำหนดรูปแบบการตอบกลับให้เป็นมาตรฐานเดียวกันทั้งระบบ
ใน Node.js เราสามารถใช้ Middleware (ฟังก์ชันที่คั่นกลางระหว่างการรับ Request และการส่ง Response) เพื่อดักจับทุกข้อผิดพลาดที่เกิดขึ้นในโปรแกรม ถ้ามีอะไรผิดพลาดเกิดขึ้น เราแค่สั่ง next(error) โปรแกรมจะส่งต่อข้อผิดพลาดนั้นมายังจุดจัดการหลักที่เราวางไว้เพียงจุดเดียว ทำให้โค้ดในส่วนอื่นสะอาดและโฟกัสแค่การทำงานหลักของมันเท่านั้น
วิธีนี้มีข้อดีคือ ถ้าเราอยากเปลี่ยนรูปแบบการแจ้งเตือน เช่น อยากเพิ่มรหัสข้อผิดพลาดหรือเปลี่ยนภาษาของข้อความ เราก็แค่มาแก้ที่จุดเดียวใน Error Middleware (ฟังก์ชันจัดการข้อผิดพลาด) ไม่ต้องไปไล่แก้ทุกไฟล์ในโปรเจกต์ ซึ่งถือเป็นวินัยที่ดีของโปรแกรมเมอร์มืออาชีพที่ช่วยประหยัดเวลาในการบำรุงรักษาโค้ดในระยะยาว
// ตัวอย่าง Error Middleware
app.use((err, req, res, next) => {
console.error(err.message); // พิมพ์ข้อผิดพลาดลงใน Console
res.status(400).json({
status: "error",
message: err.message,
});
});
โค้ดนี้คือฟังก์ชันที่รับพารามิเตอร์ 4 ตัว ซึ่งเป็นรูปแบบเฉพาะของ Middleware ใน Express โดย err คือข้อผิดพลาดที่ส่งมา เราใช้ res.status(400) เพื่อบอกว่าคำขอนี้ผิดพลาด และส่งข้อมูลกลับไปเป็น JSON (รูปแบบข้อมูลที่นิยมใช้ส่งผ่านเว็บ) เพื่อให้ฝั่งผู้ใช้งานนำไปแสดงผลต่อได้
เมื่อรันจริง หากมีฟังก์ชันใดในโปรแกรมเรียกใช้ next(new Error("ข้อมูลไม่ครบ")) ระบบจะมาทำงานในบล็อกนี้และส่งข้อความ {"status": "error", "message": "ข้อมูลไม่ครบ"} กลับไปที่ผู้ใช้ทันที
เปรียบเทียบระหว่าง Zod และ Joi
หลายคนอาจเคยได้ยินชื่อ Joi (ไลบรารีตรวจสอบข้อมูลรุ่นพี่) มาก่อน ซึ่งมีหน้าที่คล้ายกับ Zod มาก ทั้งสองตัวช่วยตรวจสอบข้อมูลได้ดีเยี่ยม แต่ความแตกต่างที่สำคัญคือ Zod ถูกออกแบบมาเพื่อทำงานกับ TypeScript ได้ดีกว่าตั้งแต่ต้น ทำให้เราไม่ต้องเขียนโค้ดนิยามข้อมูลซ้ำซ้อนสองรอบ ทั้งรูปแบบข้อมูลและชนิดของข้อมูล
สำหรับมือใหม่ ถ้าคุณเพิ่งเริ่มเรียนรู้ Joi ก็ยังเป็นตัวเลือกที่ดีเพราะมีเอกสารประกอบที่อ่านง่ายและใช้งานแพร่หลายในโปรเจกต์เก่าๆ แต่ถ้าคุณกำลังเริ่มโปรเจกต์ใหม่และอยากฝึกใช้เครื่องมือที่ทันสมัย Zod จะเป็นตัวเลือกที่คุ้มค่ากว่าในการลงทุนเรียนรู้ เพราะมันเป็นมาตรฐานใหม่ที่หลายบริษัทเลือกใช้ในปัจจุบัน
ไม่ว่าคุณจะเลือกตัวไหน สิ่งสำคัญไม่ใช่แค่การเลือกเครื่องมือให้ถูก แต่คือการเข้าใจว่า ทำไม เราต้องตรวจสอบข้อมูล หากคุณเข้าใจหลักการพื้นฐานแล้ว การเปลี่ยนไปใช้เครื่องมือตัวอื่นก็จะเป็นเรื่องง่ายมาก เพราะตรรกะในการทำงานของมันเหมือนกันแทบทั้งหมด คือการกำหนดกฎและเช็กข้อมูลที่ได้รับมา
ข้อควรระวังสำหรับมือใหม่
จุดที่มือใหม่มักพลาดบ่อยคือการลืมตรวจสอบข้อมูลในทุกๆ จุดที่รับค่าเข้ามา หรือตรวจสอบแบบไม่รอบคอบ เช่น การลืมเช็กว่าค่าที่ส่งมาเป็นค่าว่างหรือไม่ ทำให้เกิดข้อผิดพลาดแบบ undefined (ค่าที่ยังไม่ได้กำหนด) ในโปรแกรมจนแอปพังได้ง่ายๆ ดังนั้นต้องฝึกให้เป็นนิสัยว่าทุกครั้งที่เห็น req.body ต้องมีขั้นตอนตรวจสอบเสมอ
อีกเรื่องคือการแสดงข้อมูลข้อผิดพลาดมากเกินไป บางครั้งเราเผลอส่งรายละเอียดระบบ เช่น ชื่อไฟล์หรือชื่อฟังก์ชันที่ผิดพลาดกลับไปให้ผู้ใช้ ซึ่งเป็นช่องโหว่ทางความปลอดภัย (Security Risk) ที่แฮกเกอร์อาจใช้ประโยชน์ได้ ควรแสดงแค่ข้อความที่จำเป็นและเข้าใจง่ายสำหรับผู้ใช้ทั่วไปก็พอ ส่วนรายละเอียดเชิงลึกให้เก็บไว้ใน Log (บันทึกการทำงานของระบบ) บนเซิร์ฟเวอร์แทน
การฝึกเขียน Validation (การตรวจสอบข้อมูล) ให้เป็นส่วนหนึ่งของขั้นตอนการพัฒนา จะทำให้คุณกลายเป็นโปรแกรมเมอร์ที่เขียนโค้ดได้รัดกุมและมีความรับผิดชอบต่อคุณภาพของซอฟต์แวร์มากขึ้น เริ่มต้นจากการตรวจสอบง่ายๆ แล้วค่อยๆ ขยับไปใช้ระบบที่ซับซ้อนขึ้นเมื่อคุณเริ่มมีความชำนาญมากขึ้นครับ
สรุป
การตรวจสอบข้อมูลด้วย Zod และการทำ Centralized Error Handling คือหัวใจสำคัญของการเขียน Backend ที่มีคุณภาพ มันช่วยให้โค้ดของคุณสะอาด ปลอดภัย และดูแลรักษาง่ายขึ้นมาก ไม่ว่าจะเป็นโปรเจกต์เล็กๆ หรือระบบขนาดใหญ่ การวางโครงสร้างเหล่านี้ไว้ตั้งแต่ต้นจะช่วยลดจำนวนบั๊กที่คุณต้องตามแก้ในอนาคตได้อย่างมหาศาล
ลองนำความรู้ในบทความนี้ไปใช้กับโปรเจกต์ของคุณดูครับ เช่น ถ้าคุณกำลังทำระบบ "สมุดจดบันทึก" ให้ลองเพิ่มการตรวจสอบว่าหัวข้อบันทึกต้องไม่ว่าง และถ้าบันทึกไม่สำเร็จให้ส่งข้อความเตือนกลับไปหาผู้ใช้ผ่านระบบจัดการข้อผิดพลาดที่เราสร้างขึ้น เพียงเท่านี้คุณก็จะเห็นภาพการทำงานของระบบที่ดูเป็นมืออาชีพมากขึ้นแล้ว
จงจำไว้ว่าโปรแกรมเมอร์ที่เก่งไม่ได้วัดกันที่เขียนโค้ดเร็วแค่ไหน แต่อยู่ที่การเขียนโค้ดที่รัดกุมและจัดการกับปัญหาได้อย่างเป็นระบบ การฝึกฝนทำสิ่งเหล่านี้ให้เป็นนิสัยจะทำให้คุณก้าวข้ามจากมือใหม่ไปสู่ระดับที่ทีมงานไว้วางใจได้อย่างแน่นอนครับ