ทรัพยากร
ทรัพยากรแนวปฏิบัติที่ดีที่สุดของ GraphQL

แนวปฏิบัติที่ดีที่สุดของ GraphQL

GraphQL มีความเป็นผู้ใหญ่เพียงพอและได้รับการใช้งานมาอย่างยาวนาน จนชุมชนได้เผยแพร่บทความแบ่งปันแนวปฏิบัติที่ดีที่สุดมากมาย คู่มือเหล่านี้ครอบคลุมเกือบทุกแง่มุมของ GraphQL ไม่ว่าจะเป็นการออกแบบ schema, หลักการตั้งชื่อ, การจัดการความปลอดภัย และการให้ข้อความแสดงข้อผิดพลาดที่มีความหมาย

ต่อไปนี้คือคู่มือที่น่าสนใจที่สุดเกี่ยวกับแนวปฏิบัติที่ดีที่สุดใน GraphQL

แนวปฏิบัติที่ดีที่สุดบน graphql.org

เว็บไซต์ GraphQL อย่างเป็นทางการมีบทนำทั่วไปเกี่ยวกับแนวปฏิบัติที่ดีที่สุดใน GraphQL

รายการเหล่านี้ครอบคลุมประเด็นระดับสูงเป็นหลัก เช่น:

ตำแหน่งที่ GraphQL layer ถูกวางไว้ในสถาปัตยกรรม

คำแนะนำของ 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) และนำเสนอทั้งหมดในรูปแบบที่กระชับ

กฎที่อธิบายไว้ประกอบด้วย:

  1. กฎการตั้งชื่อ
    • ใช้ camelCase สำหรับ GraphQL fields และ arguments
    • ใช้ UpperCamelCase สำหรับ GraphQL types
    • ใช้ CAPITALIZED_WITH_UNDERSCORES สำหรับชื่อ ENUM types
  2. กฎของ type
    • ใช้ custom scalar types หากต้องการประกาศ fields หรือ args ที่มีค่า semantic เฉพาะ
    • ใช้ Enum สำหรับ fields ที่มีชุดค่าเฉพาะ
  3. กฎของ Field (Output)
    • ใช้ชื่อ semantic สำหรับ fields และหลีกเลี่ยงการเปิดเผยรายละเอียดการ implement ในชื่อ fields
    • ใช้ Non-Null field หาก field นั้นจะมีค่าเสมอ
    • จัดกลุ่ม fields ที่เกี่ยวข้องไว้ใน custom Object type ให้มากที่สุดเท่าที่เป็นไปได้
  4. กฎของ argument (Input)
    • จัดกลุ่ม arguments ที่เกี่ยวข้องกันไว้ใน input-type ใหม่
    • ใช้ scalar types ที่เข้มงวดสำหรับ arguments เช่น DateTime แทน String
    • ทำเครื่องหมาย arguments ว่า required หากจำเป็นสำหรับการ execute query
  5. กฎของ 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
  6. กฎของ 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 ได้แก่:

  1. Injection — ซึ่งโดยทั่วไปรวมถึงแต่ไม่จำกัดเพียง:
    • SQL และ NoSQL injection
    • OS Command injection
    • SSRF และ CRLF injection/Request Smuggling
  2. DoS (Denial of Service)
  3. การใช้ประโยชน์จาก broken authorization: การเข้าถึงที่ไม่เหมาะสมหรือเกินขอบเขต รวมถึง IDOR
  4. Batching Attacks วิธีการโจมตีแบบ brute force ที่เฉพาะเจาะจงกับ GraphQL
  5. การใช้ประโยชน์จากการตั้งค่า 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:

  1. One Graph: บริษัทควรมี graph รวมเดียว แทนที่จะมีหลาย graph ที่สร้างโดยแต่ละทีม
  2. Federated Implementation: แม้จะมีเพียง graph เดียว แต่การ implement graph นั้นควรถูก federate ข้ามหลายทีม
  3. Track the Schema in a Registry: ควรมีแหล่งข้อมูลเดียวสำหรับการลงทะเบียนและติดตาม graph
  4. Abstract, Demand-Oriented Schema: Schema ควรทำหน้าที่เป็น abstraction layer ที่ให้ความยืดหยุ่นแก่ผู้ใช้ในขณะที่ซ่อนรายละเอียดการ implement ของ service
  5. Use an Agile Approach to Schema Development: Schema ควรถูกสร้างขึ้นทีละขั้นตามความต้องการจริง และพัฒนาได้อย่างราบรื่นตามกาลเวลา
  6. Iteratively Improve Performance: การจัดการ performance ควรเป็นกระบวนการที่ขับเคลื่อนด้วยข้อมูลอย่างต่อเนื่อง ปรับตัวได้ราบรื่นตาม query loads และ service implementations ที่เปลี่ยนแปลง
  7. Use Graph Metadata to Empower Developers: นักพัฒนาควรมีความตระหนักรู้อย่างละเอียดเกี่ยวกับ graph ตลอดกระบวนการพัฒนาทั้งหมด
  8. Access and Demand Control: ให้สิทธิ์การเข้าถึง graph ตาม per-client และจัดการว่า clients สามารถเข้าถึงอะไรและอย่างไร
  9. Structured Logging: รวบรวม structured logs ของ graph operations ทั้งหมดและใช้เป็นเครื่องมือหลักในการทำความเข้าใจการใช้งาน graph
  10. Separate the GraphQL Layer from the Service Layer: ใช้สถาปัตยกรรมแบบ layered ที่แยกฟังก์ชัน graph ออกเป็นชั้นแยกต่างหาก แทนที่จะฝังไว้ในทุก service