ข้ามไปยังเนื้อหา

Errors & Validation

API ส่วนใหญ่มองความล้มเหลวเป็นช่องทางแยก ไม่ว่าจะเป็น 404, 500 หรือ body ที่ต้องแกะคนละแบบตอนมีอะไรพัง แต่ GraphQL เลือกทางที่ต่างออกไป — response เดียวอธิบายได้ทั้งสิ่งที่สำเร็จและสิ่งที่ล้มเหลว ในรูปร่างเดียว พร้อมกัน รูปร่างนี้แหละคือหัวใจของโมดูลนี้

GraphQL response คือ JSON object ที่มี top-level key ได้มากสุดสามตัว ได้แก่ data, errors และ extensions ตัวแรกเก็บทุกอย่างที่ server resolve สำเร็จ ตัวที่สองคือรายการของทุกอย่างที่พัง สอง key นี้ไม่ได้เลือกได้อย่างใดอย่างหนึ่ง response หนึ่งพก data tree ที่มีข้อมูลเต็ม พร้อมกับ errors array ที่ไม่ว่างมาด้วยกันได้

ตลอดคอร์สนี้เราเรียก schema ว่า contract และ errors ก็เขียนอยู่ใน contract นั้นด้วย ตัว spec กำหนดรูปร่างของ error object ไว้ตายตัว กำหนดกฎว่า null โผล่ที่ไหนได้บ้างเมื่อ field หนึ่งล้มเหลว และรับประกันว่าความสำเร็จระดับ transport (HTTP 200) ยังห่อ operation ที่ล้มเหลวไว้ข้างในได้ ฝั่ง client จึงต้องอ่าน errors ไม่ใช่แค่ data

พอซึมซับแนวคิดนี้ได้ มุมมองจะเปลี่ยนไปเลย คุณจะเลิกถามแบบใช่/ไม่ใช่ว่า “request สำเร็จไหม” แล้วหันมาถามว่า “ส่วนไหนสำเร็จ ส่วนไหนพัง และความพังแต่ละอันบอกให้ client ทำอะไรต่อ” GraphQL API ที่ดีตอบทั้งสามคำถามนี้ได้ใน round trip เดียว

flowchart TD
  Response["GraphQL response (JSON)"]
  Data["data — what resolved"]
  Errors["errors — list of failures"]
  Extensions["extensions — optional metadata"]
  Response --> Data
  Response --> Errors
  Response --> Extensions
  Errors -->|"path"| Data
GraphQL response แยกออกเป็น data, errors และ extensions ที่เป็นทางเลือก

ไดอะแกรมแตกออกเป็นสามกิ่ง กิ่ง data สะท้อนรูปร่างของ query กิ่ง errors เป็นรายการแบน ๆ แต่ละ entry ชี้กลับเข้าไปใน data ผ่าน path ส่วน extensions เป็นถุงเปล่าที่ server แนบรายละเอียดแบบเครื่องอ่านได้เข้าไป และเรามีบทเรียนหนึ่งเต็ม ๆ ให้เรื่องนี้

โมดูลนี้ว่าด้วยด้านความล้มเหลวของ contract ทั้งห้าบทเรียนได้แก่

  1. Errors & Validation (คุณอยู่ที่นี่) — รูปร่าง response แบบ data/errors และทำไม errors ถึงเป็น first-class
  2. GraphQL Errors — เจาะลึก errors array ทั้ง message, locations, path และความต่างระหว่าง request errors กับ field errors
  3. Partial Results — nullability คุมการแพร่ของ error อย่างไร และทำไม field พี่น้องยัง resolve ต่อได้แม้ field หนึ่งพัง
  4. Error Extensions — typed errors ด้วย extensions การ throw GraphQLError และการซ่อนรายละเอียดภายในบน production
  5. Input Validation — สิ่งที่ schema validate ให้ฟรี เทียบกับ business rule ที่คุณต้องบังคับเองใน resolver

ทฤษฎีพอแล้ว ตัว runner ด้านล่างนิยาม schema ที่มี field เดียว และ resolver ของ field นั้น throw กด Run แล้วอ่านผลลัพธ์ สังเกตว่า data ยังอยู่ (เป็น null ตรง field ที่พัง) และ errors อธิบายว่าเกิดอะไรขึ้น

JavaScript

อ่าน output เป็น object เดียว ค่า data.currentTrack กลับมาเป็น null เพราะ resolver throw และ field นั้น nullable ถัดมา errors เก็บ entry เดียวที่อธิบายความล้มเหลว ไม่มี key ไหนหักล้างกันเลย การอยู่ร่วมกันแบบนี้แหละที่ทำให้การจัดการ error ของ GraphQL ต่างจาก HTTP status code ธรรมดา

top-level key ใดบ้างที่ GraphQL response เดียวสามารถมีได้?
response หนึ่งพก data tree ที่มีข้อมูล พร้อมกับ errors array ที่ไม่ว่าง มาด้วยกันได้ไหม?
ใน contract นั้น errors อธิบายได้ดีที่สุดว่าเป็นอะไร?