แนวปฏิบัติที่ดีที่สุดของ GraphQL
GraphQL มีความเป็นผู้ใหญ่เพียงพอและได้รับการใช้งานมาอย่างยาวนาน จนชุมชนได้เผยแพร่บทความแบ่งปันแนวปฏิบัติที่ดีที่สุดมากมาย คู่มือเหล่านี้ครอบคลุมเกือบทุกแง่มุมของ GraphQL ไม่ว่าจะเป็นการออกแบบ schema, หลักการตั้งชื่อ, การจัดการความปลอดภัย และการให้ข้อความแสดงข้อผิดพลาดที่มีความหมาย
ต่อไปนี้คือคู่มือที่น่าสนใจที่สุดเกี่ยวกับแนวปฏิบัติที่ดีที่สุดใน GraphQL
แนวปฏิบัติที่ดีที่สุดบน graphql.org
เว็บไซต์ GraphQL อย่างเป็นทางการมีบทนำทั่วไปเกี่ยวกับแนวปฏิบัติที่ดีที่สุดใน GraphQL
รายการเหล่านี้ครอบคลุมประเด็นระดับสูงเป็นหลัก เช่น:
- วิธีที่ดีที่สุดในการแบ่งหน้าผลลัพธ์
- ตำแหน่งของ GraphQL layer ภายในสถาปัตยกรรม
- วิธีใช้ Node interface สำหรับการระบุ object แบบ global
- วิธีแคชผลลัพธ์
- และอื่นๆ อีกมากมาย

คำแนะนำของ Lee Byron
ไม่นานหลังจากเผยแพร่ GraphQL สู่โลก Lee Byron ผู้สร้าง GraphQL ได้เผยแพร่บทความ Lessons From 4 Years of GraphQL โดยอธิบายว่าเราควรพยายามทำงานกับ GraphQL อย่างไรในเชิงแนวคิด:
- การตั้งชื่อมีความสำคัญ
- คิดในแง่ของ graph ไม่ใช่ endpoint
- อธิบายข้อมูล ไม่ใช่ view
- GraphQL คือ interface บางๆ
- ซ่อนรายละเอียดการ implement
เขายังได้อธิบายหลักการและบทเรียนอันมีคุณค่าที่เขาเรียนรู้ขณะใช้ GraphQL ใน Facebook อย่างละเอียด
GraphQL Rules
GraphQL Rules คือเว็บไซต์ที่อุทิศตัวเพื่อนำเสนอแนวปฏิบัติที่ดีที่สุดในชีวิตประจำวันสำหรับการทำงานกับ GraphQL โดยเน้นเป็นหลักที่การออกแบบ GraphQL schema
แหล่งข้อมูลนี้มีความละเอียดมาก มันรวบรวมข้อมูลจากแหล่งข้อมูลชั้นเยี่ยมหลายแหล่ง (เช่น บทความ Designing GraphQL Mutations และบทช่วยสอนของ Shopify Designing a GraphQL API) และนำเสนอทั้งหมดในรูปแบบที่กระชับ
กฎที่อธิบายไว้ประกอบด้วย:
- กฎการตั้งชื่อ
- ใช้
camelCaseสำหรับ GraphQL fields และ arguments - ใช้
UpperCamelCaseสำหรับ GraphQL types - ใช้
CAPITALIZED_WITH_UNDERSCORESสำหรับชื่อ ENUM types
- ใช้
- กฎของ type
- ใช้ custom scalar types หากต้องการประกาศ fields หรือ args ที่มีค่า semantic เฉพาะ
- ใช้ Enum สำหรับ fields ที่มีชุดค่าเฉพาะ
- กฎของ Field (Output)
- ใช้ชื่อ semantic สำหรับ fields และหลีกเลี่ยงการเปิดเผยรายละเอียดการ implement ในชื่อ fields
- ใช้
Non-Nullfield หาก field นั้นจะมีค่าเสมอ - จัดกลุ่ม fields ที่เกี่ยวข้องไว้ใน custom Object type ให้มากที่สุดเท่าที่เป็นไปได้
- กฎของ argument (Input)
- จัดกลุ่ม arguments ที่เกี่ยวข้องกันไว้ใน input-type ใหม่
- ใช้ scalar types ที่เข้มงวดสำหรับ arguments เช่น
DateTimeแทนString - ทำเครื่องหมาย arguments ว่า
requiredหากจำเป็นสำหรับการ execute query
- กฎของ list
- สำหรับการกรอง list ให้ใช้ argument
filterที่มีตัวกรองที่ใช้ได้ทั้งหมด - ใช้ argument
sortประเภทEnumหรือ[Enum!]สำหรับการเรียงลำดับ listing - ใช้
limitที่มีค่า default และskipเพื่อจำกัดจำนวนรายการที่ส่งคืนใน list - ใช้ args
pageและperPageสำหรับ pagination และส่งคืน output type ที่มีitems(array ของ elements) และpageInfo(meta-data) - สำหรับ list แบบ infinite (infinite scroll) ให้ใช้ Relay Cursor Connections Specification
- สำหรับการกรอง list ให้ใช้ argument
- กฎของ Mutation
- ใช้ Namespace-types เพื่อจัดกลุ่ม mutations ภายใน resource เดียว
- ก้าวข้าม CRUD — สร้าง mutations ขนาดเล็กสำหรับ business operations ต่างๆ บน resources
- พิจารณาความสามารถในการ execute mutations บนหลาย items (การเปลี่ยนแปลงแบบ batch สำหรับ type เดียวกัน)
- Mutations ควรอธิบาย arguments บังคับทั้งหมดอย่างชัดเจน ไม่ควรมีตัวเลือกแบบ either-either
- ใน mutations ให้ใส่ตัวแปรทั้งหมดไว้ใน input argument unique เดียว
- ทุก mutation ควรมี payload type ที่ unique
แนวปฏิบัติที่ดีที่สุดสำหรับ resolvers
บทความ GraphQL Resolvers: Best Practices อธิบายวิธีสร้าง field resolvers ที่ดีที่สุด แม้ว่าจะมุ่งเป้าไปที่ Node.js servers (โครงสร้างพื้นฐานของ PayPal ใช้ Express) แต่บทเรียนหลายข้อสามารถนำไปใช้กับเทคโนโลยีอื่นๆ รวมถึง PHP ได้เช่นกัน
ประเด็นสำคัญหลักๆ ได้แก่:
- การดึงข้อมูลและส่งต่อจาก parent ไปยัง child ควรใช้อย่างระมัดระวัง
- ใช้ libraries เช่น dataloader เพื่อลบ requests ที่ซ้ำกันในส่วน downstream
- ตระหนักถึงแรงกดดันที่คุณสร้างให้กับแหล่งข้อมูลของคุณ
- อย่า mutate "context" เพื่อให้ได้โค้ดที่สม่ำเสมอและมีข้อผิดพลาดน้อยลง
- เขียน resolvers ที่อ่านได้ง่าย บำรุงรักษาได้ และทดสอบได้ ไม่ซับซ้อนเกินไป
- ทำให้ resolvers บางที่สุดเท่าที่เป็นไปได้ แยก logic การดึงข้อมูลออกไปเป็น async functions ที่ใช้ซ้ำได้
OWASP - GraphQL Cheat Sheet
OWASP (Open Web Application Security Project) คือมูลนิธิไม่แสวงหากำไรที่ทำงานเพื่อปรับปรุงความปลอดภัยของซอฟต์แวร์ โดยทำการวิจัยว่าเทคโนโลยีต่างๆ มีความเสี่ยงต่อการถูกโจมตีอย่างไร และอธิบายวิธีแก้ปัญหาด้านความปลอดภัยอย่างละเอียด เพื่อให้นักพัฒนาป้องกันการโจมตีได้ง่ายขึ้น
OWASP ได้เผยแพร่ GraphQL Cheat Sheet ที่อธิบายว่าการโจมตีที่พบบ่อยที่สุดและปัญหาความปลอดภัยที่ใหญ่ที่สุดใน GraphQL คืออะไร และวิธีแก้ไขปัญหาเหล่านั้น
การโจมตีทั่วไปต่อ GraphQL ได้แก่:
- Injection — ซึ่งโดยทั่วไปรวมถึงแต่ไม่จำกัดเพียง:
- SQL และ NoSQL injection
- OS Command injection
- SSRF และ CRLF injection/Request Smuggling
- DoS (Denial of Service)
- การใช้ประโยชน์จาก broken authorization: การเข้าถึงที่ไม่เหมาะสมหรือเกินขอบเขต รวมถึง IDOR
- Batching Attacks วิธีการโจมตีแบบ brute force ที่เฉพาะเจาะจงกับ GraphQL
- การใช้ประโยชน์จากการตั้งค่า default ที่ไม่ปลอดภัย
OWASP จากนั้นให้คำแนะนำเกี่ยวกับวิธีหลีกเลี่ยงแต่ละข้อเหล่านั้น
แนวปฏิบัติที่ดีที่สุดสำหรับ GraphQL queries
ทีม Apollo ได้เผยแพร่ GraphQL query best practices ซึ่งให้ข้อมูลเชิงลึกเชิงปฏิบัติเกี่ยวกับวิธีสร้าง GraphQL query
ตัวอย่างเช่น queries ทั้งสองนี้บรรลุเป้าหมายเดียวกัน แต่เนื่องจาก query แรกมีชื่อ operation จึงเข้าใจง่ายกว่าและเป็นประโยชน์มากกว่าเมื่อ debug:
# Recommended ✅
query GetBooks {
books {
title
}
}
# Not recommended ❌
query {
books {
title
}
}คำแนะนำของพวกเขาประกอบด้วย:
- ตั้งชื่อ operations ทั้งหมด
- ใช้ตัวแปรเพื่อระบุ GraphQL arguments
- Query เฉพาะข้อมูลที่คุณต้องการ ในที่ที่คุณต้องการ
- ใช้ fragments เพื่อ encapsulate ชุด fields ที่เกี่ยวข้อง
- Query ข้อมูล global และข้อมูลเฉพาะผู้ใช้แยกกัน
การใช้ประโยชน์จาก one graph
เว็บไซต์ Principled GraphQL จากทีม Apollo เช่นกัน อธิบายว่า GraphQL ไม่ใช่แค่ specification เท่านั้น แต่ที่สำคัญกว่านั้น คือ interface สำหรับการโต้ตอบกับ "graph" ข้อมูลของบริษัทเรา
ผ่านรายการหลักการ 10 ข้อ เว็บไซต์นี้อธิบายวิธีใช้ประโยชน์สูงสุดจาก single graph:
- One Graph: บริษัทควรมี graph รวมเดียว แทนที่จะมีหลาย graph ที่สร้างโดยแต่ละทีม
- Federated Implementation: แม้จะมีเพียง graph เดียว แต่การ implement graph นั้นควรถูก federate ข้ามหลายทีม
- Track the Schema in a Registry: ควรมีแหล่งข้อมูลเดียวสำหรับการลงทะเบียนและติดตาม graph
- Abstract, Demand-Oriented Schema: Schema ควรทำหน้าที่เป็น abstraction layer ที่ให้ความยืดหยุ่นแก่ผู้ใช้ในขณะที่ซ่อนรายละเอียดการ implement ของ service
- Use an Agile Approach to Schema Development: Schema ควรถูกสร้างขึ้นทีละขั้นตามความต้องการจริง และพัฒนาได้อย่างราบรื่นตามกาลเวลา
- Iteratively Improve Performance: การจัดการ performance ควรเป็นกระบวนการที่ขับเคลื่อนด้วยข้อมูลอย่างต่อเนื่อง ปรับตัวได้ราบรื่นตาม query loads และ service implementations ที่เปลี่ยนแปลง
- Use Graph Metadata to Empower Developers: นักพัฒนาควรมีความตระหนักรู้อย่างละเอียดเกี่ยวกับ graph ตลอดกระบวนการพัฒนาทั้งหมด
- Access and Demand Control: ให้สิทธิ์การเข้าถึง graph ตาม per-client และจัดการว่า clients สามารถเข้าถึงอะไรและอย่างไร
- Structured Logging: รวบรวม structured logs ของ graph operations ทั้งหมดและใช้เป็นเครื่องมือหลักในการทำความเข้าใจการใช้งาน graph
- Separate the GraphQL Layer from the Service Layer: ใช้สถาปัตยกรรมแบบ layered ที่แยกฟังก์ชัน graph ออกเป็นชั้นแยกต่างหาก แทนที่จะฝังไว้ในทุก service