การเขียน Query
GraphQL query คือข้อความ และข้อความนั้นมีรูปร่าง เมื่อคุณอ่านรูปร่างนั้นออกและเขียนขึ้นเองได้ คุณก็คุยกับ GraphQL API ตัวไหนก็ได้ บทเรียนนี้ประกอบ query ขึ้นมาทีละชิ้น เริ่มจาก field ต่อด้วย nested selection set แล้วจบที่ operation name ที่ห่อทุกอย่างเข้าด้วยกัน
หน่วยที่เล็กที่สุดของ query คือ field ชื่อเดียวที่ร้องขอข้อมูลหนึ่งชิ้น เราห่อ field ไว้ในปีกกา ที่เรียกว่า selection set โดย selection set บนสุดของ query จะเลือกข้อมูลจาก Query root
{ currentUser}query นี้ร้องขอ field ตัวเดียวคือ currentUser ถ้า field คืน scalar อย่าง String หรือ Int แค่ระบุชื่อก็พอ เพราะคุณมาถึง leaf แล้ว query จึงหยุดตรงนั้น ถ้าอยากขอหลาย field เคียงกัน ก็แค่ไล่ชื่อเรียงลงมา
{ serverTime apiVersion}nested selection set
หัวข้อที่มีชื่อว่า “nested selection set”เมื่อ field คืน object type แทนที่จะเป็น scalar การระบุแค่ชื่อยังไม่พอ เพราะ GraphQL ต้องรู้ว่าคุณต้องการ field ตัวไหน ของ object นั้น คุณตอบคำถามนี้ด้วยการเปิด nested selection set ที่มีปีกกาของตัวเอง
{ currentUser { id name email }}currentUser คืน User object จึงต้องตามด้วย selection set ที่ระบุ field ของ User ที่คุณสนใจ การซ้อนแบบนี้ลึกได้เท่าที่ graph เปิดให้ ถ้า User มี field address ที่คืน Address object คุณก็ซ้อนลงไปอีกชั้น
{ currentUser { name address { city country } }}ปีกกาแต่ละชั้นก้าวลึกลงไปใน graph อีกหนึ่ง edge ดังนั้น query จึงเป็น tree ของ field ที่สะท้อนรูปร่างของ response ที่คุณจะได้กลับมา
flowchart TD
Root["{ } selection set (Query root)"] --> CU["currentUser (User)"]
CU --> Name["name (scalar leaf)"]
CU --> Addr["address (Address)"]
Addr --> City["city (scalar leaf)"]
Addr --> Country["country (scalar leaf)"] จำหลักง่าย ๆ ไว้ว่า field ที่คืน object type ต้องมี selection set ส่วน field ที่คืน scalar ห้ามมี selection set ถ้าลืมกฎข้อใดข้อหนึ่ง server จะปฏิเสธ query ตั้งแต่ตอน validate ก่อนที่ resolver ตัวไหนจะได้รัน
operation name
หัวข้อที่มีชื่อว่า “operation name”รูปแบบ { ... } เปล่า ๆ เป็นรูปแบบย่อของ query คุณเขียนแบบเต็มก็ได้ และในงานจริงควรเขียนแบบเต็ม คือขึ้นต้นด้วย keyword query ตามด้วย operation name ที่คุณตั้งเอง
query GetCurrentUser { currentUser { id name }}GetCurrentUser คือ operation name ที่ไม่เปลี่ยนผลลัพธ์ แต่คุ้มค่าใน 3 ทาง — ปรากฏใน server log และ tracing ทำให้คุณแยกแยะ request ได้ จำเป็นเมื่อ document เดียวมี operation มากกว่าหนึ่งตัว และทำให้ query อธิบายตัวเองได้ การตั้งชื่อ operation เป็นนิสัยที่ควรสร้างตั้งแต่เนิ่น ๆ
รัน query ที่ซ้อนกัน
หัวข้อที่มีชื่อว่า “รัน query ที่ซ้อนกัน”ตัวอย่างด้านล่างเลือก field ที่ซ้อนกัน คือ currentUser ที่พา name กับ favoriteTrack object มาด้วย และ favoriteTrack ก็พา title ต่ออีกชั้น ตัว query ใช้รูปแบบเต็มพร้อม operation name กด Run แล้วเทียบ JSON กับ tree ของ field ที่คุณเขียน
response เป็น object ที่ซ้อนกัน โดย currentUser เก็บ name กับ favoriteTrack ส่วน favoriteTrack เก็บ title ทุกปีกกาที่คุณเปิดใน query จะสร้างการซ้อนหนึ่งระดับในผลลัพธ์ ลองลบบรรทัด title ด้านในออกดู แล้ว server จะปฏิเสธ query ทันที เพราะ field ที่คืน Track object type ต้องมี selection set
| ข้อดี | ข้อแลกเปลี่ยน |
|---|---|
| client ระบุเฉพาะ field ที่ต้องการ — ไม่รับ payload ส่วนเกิน | query syntax ใหม่สำหรับ developer ที่คุ้น REST |
| nested query ครั้งเดียวแทนหลาย REST call | deeply nested query อาจทำให้ server overload |
| operation name ช่วย debug และ monitor | ไม่มี query depth limit = security risk |
| variables ทำให้ query reuse ได้และป้องกัน injection | client ต้องเรียนรู้ fragment, alias, variable syntax |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”Query String แบบ Dynamic แทน Variable อาการ:
`query { user(id: "${userId}") { name } }`— string interpolation ใน query- เสี่ยง injection และ prevent query caching
- ใช้ variable เสมอ:
query GetUser($id: ID!) { user(id: $id) { name } }
ไม่ตั้งชื่อ Operation อาการ:
- ส่ง anonymous query เช่น
query { ... }แทนquery GetUserProfile { ... } - monitoring tool แสดงแค่ “anonymous” ทำให้ trace ยาก
- ตั้งชื่อทุก operation เพื่อ tracing และ debugging
💡 ตัวอย่างจากของจริง
GitHub:
- ทุก query ใน GitHub documentation มีชื่อ operation ชัดเจน
- ช่วยให้ rate limiting และ monitoring ทำงานได้แม่นยำ
Apollo Client:
- operation name ใช้เป็น cache key
- named query ทำให้ Apollo Studio tracking ทำงานได้