GraphQL คืออะไร
ในบทก่อนหน้า เราร่างภาพ GraphQL ไว้ในประโยคเดียว คราวนี้มาดูช้า ๆ กับสี่แนวคิดที่ทำให้ GraphQL ทำงานได้ เข้าใจสี่ข้อนี้ให้ชัด แล้วที่เหลือทั้งคอร์สก็เป็นแค่การต่อยอด
แนวคิดที่ 1 — endpoint เดียว
หัวข้อที่มีชื่อว่า “แนวคิดที่ 1 — endpoint เดียว”REST API มักขยายตัวเป็นหนึ่ง URL ต่อหนึ่ง resource เส้นทางหนึ่งสำหรับ users อีกเส้นสำหรับ posts ของพวกเขา และอีกเส้นสำหรับ comments ส่วน GraphQL API จะเปิด endpoint เดียว (ตามธรรมเนียมคือ /graphql) สำหรับการอ่านและเขียนทั้งหมด คุณไม่ได้ไล่ไปตาม URL แต่ไล่ไปตาม field ของ graph ที่มี type กำกับ ตัว endpoint อยู่นิ่ง สิ่งที่เปลี่ยนไปในแต่ละ request คือ query ที่คุณส่งเข้าไป
แนวคิดที่ 2 — schema ที่มี type กำกับคือสัญญา
หัวข้อที่มีชื่อว่า “แนวคิดที่ 2 — schema ที่มี type กำกับคือสัญญา”GraphQL API ทุกตัวนิยามด้วย schema ที่เขียนใน Schema Definition Language (SDL) schema ระบุ type, field บนแต่ละ type และค่าที่แต่ละ field คืนกลับมา นี่คือ single source of truth ที่ทั้ง client และ server ตกลงร่วมกัน:
type Author { name: String country: String}
type Book { title: String year: Int author: Author}
type Query { featuredBook: Book}เพราะ schema มี type กำกับและเครื่องอ่านได้ เครื่องมือต่าง ๆ จึง validate query ได้ตั้งแต่ก่อนรัน เติม field ให้อัตโนมัติขณะพิมพ์ และ generate type ของ TypeScript ให้เอง contract นี้ถูกบังคับใช้จริง ไม่ใช่แค่เอกสารประกอบ
แนวคิดที่ 3 — client เป็นผู้กำหนด query
หัวข้อที่มีชื่อว่า “แนวคิดที่ 3 — client เป็นผู้กำหนด query”query คือการเลือก field ที่สะท้อนรูปร่างของ schema client ตัดสินใจว่าต้องการ field ใดและจะลงลึกแค่ไหน ส่วน server สัญญาว่า response จะมีรูปร่างเดียวกับ request
flowchart LR Q["Query selection: featuredBook -> title, author -> name"] --> SRV["GraphQL server"] SRV --> RES["Response: featuredBook -> title, author -> name"] RES -.->|"same shape"| Q
แนวคิดที่ 4 — ไม่ over-fetching ไม่ under-fetching
หัวข้อที่มีชื่อว่า “แนวคิดที่ 4 — ไม่ over-fetching ไม่ under-fetching”รูปแบบความล้มเหลวสองแบบนี้เป็นแรงขับเคลื่อนการออกแบบของ GraphQL เป็นอย่างมาก:
- Over-fetching — endpoint คืนข้อมูลมากกว่าที่หน้าจอต้องการ เปลือง bandwidth และบังคับให้ client ละเลย field
- Under-fetching — endpoint คืนข้อมูลน้อยเกินไป ทำให้ client ต้องส่ง request ตามมาอีกหลายครั้งเพื่อประกอบเป็นมุมมองเดียว
เพราะ GraphQL client ระบุ field ที่ต้องการได้แม่นยำ ครอบคลุม type ที่เกี่ยวข้อง ใน round trip เดียว จึงเลี่ยงปัญหาทั้งสองข้อได้ คุณขอ title, year และ name ของ author แล้วก็ได้กลับมาเป๊ะ ๆ แค่นั้น
ดูการเลือกเฉพาะ field ที่ต้องการ
หัวข้อที่มีชื่อว่า “ดูการเลือกเฉพาะ field ที่ต้องการ”รันตัวอย่างด้านล่าง schema มี featuredBook ที่มี field ให้เลือกมากมาย แต่ query จงใจขอเพียงไม่กี่ตัว ดูว่า response มี เฉพาะ สิ่งที่ขอไป
resolver คืน object ที่มีข้อมูลมากมายทั้ง year, pages และ country ของ author แต่ไม่มีตัวใดเลยปรากฏในผลลัพธ์ query ขอเพียง title และ name ของ author ที่ซ้อนอยู่ ดังนั้นเฉพาะแค่นั้น — และเฉพาะแค่นั้น — ที่ได้กลับมา ลองเพิ่ม year เข้าไปในการเลือกแล้วรันอีกครั้ง เพื่อดู response ขยายตามให้ตรงกัน
| ข้อดี | ข้อแลกเปลี่ยน |
|---|---|
| client กำหนด field ที่ต้องการเอง — ไม่มี over/under-fetching | learning curve สูงกว่า REST — ทีมต้องเรียน schema, resolver, type system |
| single endpoint ทำให้ API surface เรียบง่าย | caching ยากกว่า — REST ใช้ HTTP cache per URL ได้ตรง ๆ |
| type system ช่วย validate ก่อน runtime | tooling setup ซับซ้อนกว่า — ต้องมี schema, code gen, playground |
| introspection ทำให้ tooling และ auto-complete ทำงานได้ | N+1 query problem ต้องจัดการด้วย DataLoader |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”เข้าใจว่า GraphQL แทน REST ทุกกรณี อาการ:
- ใช้ GraphQL สำหรับ simple CRUD API ที่ 3 endpoint ก็พอ
- เพิ่ม complexity โดยไม่ได้ประโยชน์จาก flexible query
- REST ยังดีกว่าสำหรับ public API ที่ต้องการ HTTP cache
อธิบาย GraphQL ด้วย syntax ก่อนอธิบาย problem อาการ:
- developer เรียนรู้ SDL แต่ไม่เข้าใจว่าแก้ปัญหาอะไร
- ถ้า developer ไม่เคยเจอ over-fetching จะไม่เข้าใจว่า GraphQL ช่วยอย่างไร
💡 ตัวอย่างจากของจริง
GitHub:
- เปิด public GraphQL API v4 ในปี 2016 แทน REST API v3
- developer ลด request จาก 6-7 ครั้ง เหลือ 1 ครั้งสำหรับข้อมูลเดียวกัน
Shopify Storefront API:
- GraphQL ช่วยให้ frontend team ดึงข้อมูล product, variant, collection ในครั้งเดียว
- ลด over-fetching ที่เคยเกิดกับ REST endpoint ที่ return ข้อมูล product ครบทุก field