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

GraphQL คืออะไร

ในบทก่อนหน้า เราร่างภาพ GraphQL ไว้ในประโยคเดียว คราวนี้มาดูช้า ๆ กับสี่แนวคิดที่ทำให้ GraphQL ทำงานได้ เข้าใจสี่ข้อนี้ให้ชัด แล้วที่เหลือทั้งคอร์สก็เป็นแค่การต่อยอด

REST API มักขยายตัวเป็นหนึ่ง URL ต่อหนึ่ง resource เส้นทางหนึ่งสำหรับ users อีกเส้นสำหรับ posts ของพวกเขา และอีกเส้นสำหรับ comments ส่วน GraphQL API จะเปิด endpoint เดียว (ตามธรรมเนียมคือ /graphql) สำหรับการอ่านและเขียนทั้งหมด คุณไม่ได้ไล่ไปตาม URL แต่ไล่ไปตาม field ของ graph ที่มี type กำกับ ตัว endpoint อยู่นิ่ง สิ่งที่เปลี่ยนไปในแต่ละ request คือ query ที่คุณส่งเข้าไป

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 นี้ถูกบังคับใช้จริง ไม่ใช่แค่เอกสารประกอบ

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
response สะท้อน query — field เดียวกัน การซ้อนชั้นเดียวกัน

รูปแบบความล้มเหลวสองแบบนี้เป็นแรงขับเคลื่อนการออกแบบของ GraphQL เป็นอย่างมาก:

  • Over-fetching — endpoint คืนข้อมูลมากกว่าที่หน้าจอต้องการ เปลือง bandwidth และบังคับให้ client ละเลย field
  • Under-fetching — endpoint คืนข้อมูลน้อยเกินไป ทำให้ client ต้องส่ง request ตามมาอีกหลายครั้งเพื่อประกอบเป็นมุมมองเดียว

เพราะ GraphQL client ระบุ field ที่ต้องการได้แม่นยำ ครอบคลุม type ที่เกี่ยวข้อง ใน round trip เดียว จึงเลี่ยงปัญหาทั้งสองข้อได้ คุณขอ title, year และ name ของ author แล้วก็ได้กลับมาเป๊ะ ๆ แค่นั้น

รันตัวอย่างด้านล่าง schema มี featuredBook ที่มี field ให้เลือกมากมาย แต่ query จงใจขอเพียงไม่กี่ตัว ดูว่า response มี เฉพาะ สิ่งที่ขอไป

JavaScript

resolver คืน object ที่มีข้อมูลมากมายทั้ง year, pages และ country ของ author แต่ไม่มีตัวใดเลยปรากฏในผลลัพธ์ query ขอเพียง title และ name ของ author ที่ซ้อนอยู่ ดังนั้นเฉพาะแค่นั้น — และเฉพาะแค่นั้น — ที่ได้กลับมา ลองเพิ่ม year เข้าไปในการเลือกแล้วรันอีกครั้ง เพื่อดู response ขยายตามให้ตรงกัน

ข้อดีข้อแลกเปลี่ยน
client กำหนด field ที่ต้องการเอง — ไม่มี over/under-fetchinglearning curve สูงกว่า REST — ทีมต้องเรียน schema, resolver, type system
single endpoint ทำให้ API surface เรียบง่ายcaching ยากกว่า — REST ใช้ HTTP cache per URL ได้ตรง ๆ
type system ช่วย validate ก่อน runtimetooling 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
อะไรทำหน้าที่เป็นสัญญาระหว่าง client และ server ของ GraphQL?
หน้าจอ mobile ต้องการแค่ title แต่ endpoint บังคับให้ต้องดาวน์โหลด record ทั้งหมด อาการแบบนี้เรียกว่าอะไร?
ใน GraphQL ใครเป็นผู้ตัดสินว่า field ใดจะปรากฏใน response?
โดยทั่วไป GraphQL API ใช้ endpoint กี่ตัวในการให้บริการข้อมูลทั้งหมด?