เรียนรู้รูปแบบการออกแบบ GraphQL schema ที่ขยายขนาดได้ เพื่อสร้าง API ที่แข็งแกร่งและดูแลรักษาง่าย ซึ่งตอบสนองผู้ใช้ทั่วโลกที่หลากหลาย ทำความเข้าใจ schema stitching, federation และ modularization
การออกแบบ GraphQL Schema: รูปแบบที่ขยายขนาดได้สำหรับ API ระดับโลก
GraphQL ได้กลายเป็นทางเลือกที่มีประสิทธิภาพแทน REST API แบบดั้งเดิม โดยให้ความยืดหยุ่นแก่ไคลเอ็นต์ในการร้องขอข้อมูลที่ต้องการได้อย่างแม่นยำ อย่างไรก็ตาม เมื่อ GraphQL API ของคุณเติบโตขึ้นในด้านความซับซ้อนและขอบเขต โดยเฉพาะอย่างยิ่งเมื่อให้บริการแก่ผู้ใช้ทั่วโลกที่มีความต้องการข้อมูลที่หลากหลาย การออกแบบ schema อย่างระมัดระวังจึงกลายเป็นสิ่งสำคัญอย่างยิ่งต่อการบำรุงรักษา ความสามารถในการขยายขนาด และประสิทธิภาพ บทความนี้จะสำรวจรูปแบบการออกแบบ GraphQL schema ที่ขยายขนาดได้หลายรูปแบบเพื่อช่วยให้คุณสร้าง API ที่แข็งแกร่งซึ่งสามารถรับมือกับความต้องการของแอปพลิเคชันระดับโลกได้
ความสำคัญของการออกแบบ Schema ที่ขยายขนาดได้
GraphQL schema ที่ออกแบบมาอย่างดีคือรากฐานของ API ที่ประสบความสำเร็จ มันเป็นตัวกำหนดว่าไคลเอ็นต์จะโต้ตอบกับข้อมูลและบริการของคุณได้อย่างไร การออกแบบ schema ที่ไม่ดีอาจนำไปสู่ปัญหาหลายประการ ได้แก่:
- ปัญหาคอขวดด้านประสิทธิภาพ (Performance bottlenecks): การสืบค้น (query) และ resolver ที่ไม่มีประสิทธิภาพอาจทำให้แหล่งข้อมูลของคุณทำงานหนักเกินไปและทำให้เวลาตอบสนองช้าลง
- ปัญหาด้านการบำรุงรักษา (Maintainability issues): schema แบบ monolithic (ก้อนเดียว) จะเข้าใจ แก้ไข และทดสอบได้ยากขึ้นเมื่อแอปพลิเคชันของคุณเติบโตขึ้น
- ช่องโหว่ด้านความปลอดภัย (Security vulnerabilities): การควบคุมการเข้าถึงที่กำหนดไว้อย่างไม่รัดกุมอาจเปิดเผยข้อมูลที่ละเอียดอ่อนต่อผู้ใช้ที่ไม่ได้รับอนุญาต
- ความสามารถในการขยายขนาดที่จำกัด (Limited scalability): schema ที่เชื่อมโยงกันอย่างแน่นหนาทำให้ยากต่อการกระจาย API ของคุณไปยังเซิร์ฟเวอร์หรือทีมต่างๆ
สำหรับแอปพลิเคชันระดับโลก ปัญหาเหล่านี้จะยิ่งทวีความรุนแรงขึ้น ภูมิภาคต่างๆ อาจมีความต้องการข้อมูล ข้อจำกัดด้านกฎระเบียบ และความคาดหวังด้านประสิทธิภาพที่แตกต่างกัน การออกแบบ schema ที่ขยายขนาดได้จะช่วยให้คุณสามารถจัดการกับความท้าทายเหล่านี้ได้อย่างมีประสิทธิภาพ
หลักการสำคัญของการออกแบบ Schema ที่ขยายขนาดได้
ก่อนที่จะลงลึกในรูปแบบเฉพาะ เรามาสรุปหลักการสำคัญบางประการที่ควรเป็นแนวทางในการออกแบบ schema ของคุณ:
- ความเป็นโมดูล (Modularity): แบ่ง schema ของคุณออกเป็นโมดูลขนาดเล็กและเป็นอิสระต่อกัน ซึ่งจะช่วยให้เข้าใจ แก้ไข และนำส่วนต่างๆ ของ API กลับมาใช้ใหม่ได้ง่ายขึ้น
- ความสามารถในการประกอบ (Composability): ออกแบบ schema ของคุณเพื่อให้โมดูลต่างๆ สามารถนำมารวมและขยายได้อย่างง่ายดาย ซึ่งช่วยให้คุณสามารถเพิ่มคุณสมบัติและฟังก์ชันการทำงานใหม่ๆ ได้โดยไม่กระทบต่อไคลเอ็นต์ที่มีอยู่
- ความเป็นนามธรรม (Abstraction): ซ่อนความซับซ้อนของแหล่งข้อมูลและบริการเบื้องหลังของคุณไว้ภายใต้อินเทอร์เฟซ GraphQL ที่กำหนดไว้อย่างดี ซึ่งช่วยให้คุณสามารถเปลี่ยนแปลงการ υλοποίηση (implementation) ของคุณได้โดยไม่ส่งผลกระทบต่อไคลเอ็นต์
- ความสอดคล้อง (Consistency): รักษาแบบแผนการตั้งชื่อ โครงสร้างข้อมูล และกลยุทธ์การจัดการข้อผิดพลาดที่สอดคล้องกันตลอดทั้ง schema ของคุณ ซึ่งจะช่วยให้ไคลเอ็นต์เรียนรู้และใช้งาน API ของคุณได้ง่ายขึ้น
- การเพิ่มประสิทธิภาพ (Performance Optimization): พิจารณาผลกระทบด้านประสิทธิภาพในทุกขั้นตอนของการออกแบบ schema ใช้เทคนิคต่างๆ เช่น data loader และ field aliasing เพื่อลดจำนวนการสืบค้นฐานข้อมูลและการร้องขอผ่านเครือข่าย
รูปแบบการออกแบบ Schema ที่ขยายขนาดได้
ต่อไปนี้คือรูปแบบการออกแบบ schema ที่ขยายขนาดได้หลายรูปแบบที่คุณสามารถใช้เพื่อสร้าง GraphQL API ที่แข็งแกร่ง:
1. การต่อเชื่อมสกีมา (Schema Stitching)
Schema stitching ช่วยให้คุณสามารถรวม GraphQL API หลายๆ ตัวเข้าเป็น schema เดียวที่เป็นหนึ่งเดียวได้ ซึ่งมีประโยชน์อย่างยิ่งเมื่อคุณมีทีมหรือบริการต่างๆ ที่รับผิดชอบข้อมูลในส่วนต่างๆ กัน มันเหมือนกับการมี API ขนาดเล็กหลายๆ ตัวและเชื่อมต่อเข้าด้วยกันผ่าน 'gateway' API
วิธีการทำงาน:
- แต่ละทีมหรือบริการจะเปิดเผย GraphQL API ของตนเองพร้อมกับ schema ของตนเอง
- บริการ gateway กลางจะใช้เครื่องมือ schema stitching (เช่น Apollo Federation หรือ GraphQL Mesh) เพื่อรวม schema เหล่านี้เข้าเป็น schema เดียวที่เป็นหนึ่งเดียว
- ไคลเอ็นต์จะโต้ตอบกับบริการ gateway ซึ่งจะส่งต่อคำร้องขอไปยัง API เบื้องหลังที่เหมาะสม
ตัวอย่าง:
ลองนึกภาพแพลตฟอร์มอีคอมเมิร์ซที่มี API แยกกันสำหรับสินค้า ผู้ใช้ และคำสั่งซื้อ โดยแต่ละ API มี schema ของตัวเอง:
# API สินค้า
type Product {
id: ID!
name: String!
price: Float!
}
type Query {
product(id: ID!): Product
}
# API ผู้ใช้
type User {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
}
# API คำสั่งซื้อ
type Order {
id: ID!
userId: ID!
productId: ID!
quantity: Int!
}
type Query {
order(id: ID!): Order
}
บริการ gateway สามารถต่อเชื่อม schema เหล่านี้เข้าด้วยกันเพื่อสร้าง schema ที่เป็นหนึ่งเดียว:
type Product {
id: ID!
name: String!
price: Float!
}
type User {
id: ID!
name: String!
email: String!
}
type Order {
id: ID!
user: User! @relation(field: "userId")
product: Product! @relation(field: "productId")
quantity: Int!
}
type Query {
product(id: ID!): Product
user(id: ID!): User
order(id: ID!): Order
}
สังเกตว่าชนิดข้อมูล Order
ตอนนี้มีการอ้างอิงถึง User
และ Product
แม้ว่าชนิดข้อมูลเหล่านี้จะถูกกำหนดใน API ที่แยกจากกัน ซึ่งทำได้โดยผ่าน directive ของ schema stitching (เช่น @relation
ในตัวอย่างนี้)
ข้อดี:
- การเป็นเจ้าของแบบกระจายศูนย์: แต่ละทีมสามารถจัดการข้อมูลและ API ของตนเองได้อย่างอิสระ
- ปรับปรุงความสามารถในการขยายขนาด: คุณสามารถขยายขนาดแต่ละ API ได้อย่างอิสระตามความต้องการเฉพาะของมัน
- ลดความซับซ้อน: ไคลเอ็นต์จำเป็นต้องโต้ตอบกับ API endpoint เพียงแห่งเดียว
ข้อควรพิจารณา:
- ความซับซ้อน: Schema stitching สามารถเพิ่มความซับซ้อนให้กับสถาปัตยกรรมของคุณได้
- ความหน่วง (Latency): การส่งต่อคำร้องขอผ่านบริการ gateway อาจทำให้เกิดความหน่วงได้
- การจัดการข้อผิดพลาด: คุณต้องมีการจัดการข้อผิดพลาดที่แข็งแกร่งเพื่อรับมือกับความล้มเหลวใน API เบื้องหลัง
2. สหพันธ์สกีมา (Schema Federation)
Schema federation เป็นวิวัฒนาการของ schema stitching ที่ออกแบบมาเพื่อแก้ไขข้อจำกัดบางประการของมัน โดยให้แนวทางที่เป็น declarative และเป็นมาตรฐานมากขึ้นในการประกอบ GraphQL schema
วิธีการทำงาน:
- แต่ละบริการจะเปิดเผย GraphQL API และใส่คำอธิบายประกอบ (annotate) schema ของตนด้วย directive ของ federation (เช่น
@key
,@extends
,@external
) - บริการ gateway กลาง (โดยใช้ Apollo Federation) จะใช้ directive เหล่านี้เพื่อสร้าง supergraph ซึ่งเป็นตัวแทนของ schema แบบสหพันธ์ทั้งหมด
- บริการ gateway จะใช้ supergraph เพื่อส่งต่อคำร้องขอไปยังบริการเบื้องหลังที่เหมาะสมและแก้ไขการพึ่งพากัน (dependencies)
ตัวอย่าง:
จากตัวอย่างอีคอมเมิร์ซเดียวกัน schema แบบสหพันธ์อาจมีลักษณะดังนี้:
# API สินค้า
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
}
type Query {
product(id: ID!): Product
}
# API ผู้ใช้
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
}
# API คำสั่งซื้อ
type Order {
id: ID!
userId: ID!
productId: ID!
quantity: Int!
user: User! @requires(fields: "userId")
product: Product! @requires(fields: "productId")
}
extend type Query {
order(id: ID!): Order
}
สังเกตการใช้ directive ของ federation:
@key
: ระบุคีย์หลักสำหรับชนิดข้อมูล@requires
: บ่งชี้ว่าฟิลด์ต้องการข้อมูลจากบริการอื่น@extends
: อนุญาตให้บริการขยายชนิดข้อมูลที่กำหนดในบริการอื่น
ข้อดี:
- การประกอบแบบ Declarative: directive ของ federation ทำให้เข้าใจและจัดการการพึ่งพากันของ schema ได้ง่ายขึ้น
- ประสิทธิภาพที่ดีขึ้น: Apollo Federation เพิ่มประสิทธิภาพการวางแผนและการดำเนินการสืบค้นเพื่อลดความหน่วง
- ความปลอดภัยของชนิดข้อมูลที่ดีขึ้น (Enhanced type safety): supergraph ช่วยให้มั่นใจได้ว่าชนิดข้อมูลทั้งหมดมีความสอดคล้องกันในทุกบริการ
ข้อควรพิจารณา:
- เครื่องมือ: ต้องใช้ Apollo Federation หรือการ υλοποίηση federation ที่เข้ากันได้
- ความซับซ้อน: อาจซับซ้อนในการตั้งค่ามากกว่า schema stitching
- ช่วงการเรียนรู้ (Learning curve): นักพัฒนาจำเป็นต้องเรียนรู้ directive และแนวคิดของ federation
3. การออกแบบ Schema แบบโมดูลาร์
การออกแบบ schema แบบโมดูลาร์เกี่ยวข้องกับการแบ่ง schema ขนาดใหญ่แบบ monolithic ออกเป็นโมดูลขนาดเล็กที่จัดการได้ง่ายขึ้น ซึ่งช่วยให้เข้าใจ แก้ไข และนำส่วนต่างๆ ของ API กลับมาใช้ใหม่ได้ง่ายขึ้น แม้ว่าจะไม่ได้ใช้ schema แบบสหพันธ์ก็ตาม
วิธีการทำงาน:
- ระบุขอบเขตเชิงตรรกะภายใน schema ของคุณ (เช่น ผู้ใช้, สินค้า, คำสั่งซื้อ)
- สร้างโมดูลแยกต่างหากสำหรับแต่ละขอบเขต โดยกำหนดชนิดข้อมูล, การสืบค้น, และ mutation ที่เกี่ยวข้องกับขอบเขตนั้น
- ใช้กลไกการนำเข้า/ส่งออก (ขึ้นอยู่กับการ υλοποίηση GraphQL server ของคุณ) เพื่อรวมโมดูลต่างๆ เข้าเป็น schema เดียวที่เป็นหนึ่งเดียว
ตัวอย่าง (ใช้ JavaScript/Node.js):
สร้างไฟล์แยกต่างหากสำหรับแต่ละโมดูล:
// users.graphql
type User {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
}
// products.graphql
type Product {
id: ID!
name: String!
price: Float!
}
type Query {
product(id: ID!): Product
}
จากนั้น รวมเข้าด้วยกันในไฟล์ schema หลักของคุณ:
// schema.js
const { makeExecutableSchema } = require('graphql-tools');
const { typeDefs: userTypeDefs, resolvers: userResolvers } = require('./users');
const { typeDefs: productTypeDefs, resolvers: productResolvers } = require('./products');
const typeDefs = [
userTypeDefs,
productTypeDefs,
""
];
const resolvers = {
Query: {
...userResolvers.Query,
...productResolvers.Query,
}
};
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
module.exports = schema;
ข้อดี:
- ปรับปรุงการบำรุงรักษา: โมดูลขนาดเล็กเข้าใจและแก้ไขได้ง่ายกว่า
- เพิ่มความสามารถในการนำกลับมาใช้ใหม่: สามารถนำโมดูลกลับมาใช้ใหม่ในส่วนอื่นๆ ของแอปพลิเคชันของคุณได้
- การทำงานร่วมกันที่ดีขึ้น: ทีมต่างๆ สามารถทำงานกับโมดูลต่างๆ ได้อย่างอิสระ
ข้อควรพิจารณา:
- ค่าใช้จ่ายในการดำเนินการ (Overhead): การทำเป็นโมดูลอาจเพิ่มค่าใช้จ่ายบางอย่างให้กับกระบวนการพัฒนาของคุณ
- ความซับซ้อน: คุณต้องกำหนดขอบเขตระหว่างโมดูลอย่างระมัดระวังเพื่อหลีกเลี่ยงการพึ่งพากันแบบวงกลม (circular dependencies)
- เครื่องมือ: ต้องใช้ GraphQL server implementation ที่รองรับการกำหนด schema แบบโมดูลาร์
4. Interface และ Union Types
Interface และ union types ช่วยให้คุณสามารถกำหนดชนิดข้อมูลแบบนามธรรมที่สามารถนำไป υλοποίηση โดยชนิดข้อมูลที่เป็นรูปธรรมหลายๆ ชนิดได้ ซึ่งมีประโยชน์สำหรับการแสดงข้อมูลแบบ polymorphic หรือข้อมูลที่สามารถมีรูปแบบที่แตกต่างกันได้ขึ้นอยู่กับบริบท
วิธีการทำงาน:
- กำหนด interface หรือ union type พร้อมชุดของฟิลด์ร่วมกัน
- กำหนดชนิดข้อมูลที่เป็นรูปธรรมที่ υλοποίηση interface หรือเป็นสมาชิกของ union
- ใช้ฟิลด์
__typename
เพื่อระบุชนิดข้อมูลที่เป็นรูปธรรมในขณะทำงาน
ตัวอย่าง:
interface Node {
id: ID!
}
type User implements Node {
id: ID!
name: String!
email: String!
}
type Product implements Node {
id: ID!
name: String!
price: Float!
}
union SearchResult = User | Product
type Query {
node(id: ID!): Node
search(query: String!): [SearchResult!]!
}
ในตัวอย่างนี้ ทั้ง User
และ Product
υλοποίηση interface Node
ซึ่งกำหนดฟิลด์ร่วมกันคือ id
ชนิดข้อมูล union SearchResult
แทนผลการค้นหาที่อาจเป็น User
หรือ Product
ก็ได้ ไคลเอ็นต์สามารถสืบค้นฟิลด์ `search` แล้วใช้ฟิลด์ `__typename` เพื่อพิจารณาว่าได้รับผลลัพธ์ชนิดใด
ข้อดี:
- ความยืดหยุ่น: ช่วยให้คุณสามารถแสดงข้อมูล polymorphic ได้อย่างปลอดภัยตามชนิดข้อมูล
- การใช้โค้ดซ้ำ: ลดการทำซ้ำของโค้ดโดยการกำหนดฟิลด์ร่วมกันใน interface และ union
- ปรับปรุงความสามารถในการสืบค้น: ทำให้ไคลเอ็นต์สืบค้นข้อมูลชนิดต่างๆ ได้ง่ายขึ้นโดยใช้การสืบค้นเพียงครั้งเดียว
ข้อควรพิจารณา:
- ความซับซ้อน: สามารถเพิ่มความซับซ้อนให้กับ schema ของคุณได้
- ประสิทธิภาพ: การ resolve interface และ union types อาจมีค่าใช้จ่ายสูงกว่าการ resolve ชนิดข้อมูลที่เป็นรูปธรรม
- การตรวจสอบภายใน (Introspection): ต้องการให้ไคลเอ็นต์ใช้ introspection เพื่อกำหนดชนิดข้อมูลที่เป็นรูปธรรมในขณะทำงาน
5. รูปแบบ Connection
รูปแบบ connection เป็นวิธีมาตรฐานในการ υλοποίηση การแบ่งหน้า (pagination) ใน GraphQL API ซึ่งเป็นวิธีที่สอดคล้องและมีประสิทธิภาพในการดึงรายการข้อมูลขนาดใหญ่เป็นส่วนๆ
วิธีการทำงาน:
- กำหนดชนิดข้อมูล connection ที่มีฟิลด์
edges
และpageInfo
- ฟิลด์
edges
ประกอบด้วยรายการของ edge ซึ่งแต่ละ edge จะมีฟิลด์node
(ข้อมูลจริง) และฟิลด์cursor
(ตัวระบุที่ไม่ซ้ำกันสำหรับ node) - ฟิลด์
pageInfo
ประกอบด้วยข้อมูลเกี่ยวกับหน้าปัจจุบัน เช่น มีหน้าต่อไปหรือไม่ และ cursor สำหรับ node แรกและสุดท้าย - ใช้อาร์กิวเมนต์
first
,after
,last
, และbefore
เพื่อควบคุมการแบ่งหน้า
ตัวอย่าง:
type User {
id: ID!
name: String!
email: String!
}
type UserEdge {
node: User!
cursor: String!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Query {
users(first: Int, after: String, last: Int, before: String): UserConnection!
}
ข้อดี:
- การแบ่งหน้าที่เป็นมาตรฐาน: เป็นวิธีที่สอดคล้องกันในการ υλοποίηση การแบ่งหน้าทั่วทั้ง API ของคุณ
- การดึงข้อมูลที่มีประสิทธิภาพ: ช่วยให้คุณดึงรายการข้อมูลขนาดใหญ่เป็นส่วนๆ ลดภาระบนเซิร์ฟเวอร์และปรับปรุงประสิทธิภาพ
- การแบ่งหน้าตาม Cursor: ใช้ cursor เพื่อติดตามตำแหน่งของแต่ละ node ซึ่งมีประสิทธิภาพมากกว่าการแบ่งหน้าตาม offset
ข้อควรพิจารณา:
- ความซับซ้อน: สามารถเพิ่มความซับซ้อนให้กับ schema ของคุณได้
- ค่าใช้จ่ายในการดำเนินการ (Overhead): ต้องใช้ฟิลด์และชนิดข้อมูลเพิ่มเติมเพื่อ υλοποίηση รูปแบบ connection
- การนำไปใช้ (Implementation): ต้องมีการ υλοποίηση อย่างระมัดระวังเพื่อให้แน่ใจว่า cursor ไม่ซ้ำกันและสอดคล้องกัน
ข้อควรพิจารณาสำหรับระดับโลก
เมื่อออกแบบ GraphQL schema สำหรับผู้ใช้ทั่วโลก ให้พิจารณาปัจจัยเพิ่มเติมเหล่านี้:
- การแปลภาษา (Localization): ใช้ directive หรือ custom scalar type เพื่อรองรับภาษาและภูมิภาคต่างๆ ตัวอย่างเช่น คุณสามารถมี custom scalar `LocalizedText` ที่จัดเก็บคำแปลสำหรับภาษาต่างๆ
- เขตเวลา (Time zones): จัดเก็บการประทับเวลา (timestamp) ในรูปแบบ UTC และอนุญาตให้ไคลเอ็นต์ระบุเขตเวลาของตนเองเพื่อวัตถุประสงค์ในการแสดงผล
- สกุลเงิน (Currencies): ใช้รูปแบบสกุลเงินที่สอดคล้องกันและอนุญาตให้ไคลเอ็นต์ระบุสกุลเงินที่ต้องการเพื่อวัตถุประสงค์ในการแสดงผล พิจารณาใช้ custom scalar `Currency` เพื่อแสดงข้อมูลนี้
- ถิ่นที่อยู่ของข้อมูล (Data residency): ตรวจสอบให้แน่ใจว่าข้อมูลของคุณถูกจัดเก็บตามข้อบังคับท้องถิ่น ซึ่งอาจต้องมีการปรับใช้ API ของคุณในหลายภูมิภาคหรือใช้เทคนิคการปิดบังข้อมูล (data masking)
- การเข้าถึงได้ (Accessibility): ออกแบบ schema ของคุณให้ผู้ใช้ที่มีความพิการสามารถเข้าถึงได้ ใช้ชื่อฟิลด์ที่ชัดเจนและสื่อความหมาย และจัดเตรียมวิธีทางเลือกในการเข้าถึงข้อมูล
ตัวอย่างเช่น พิจารณาฟิลด์คำอธิบายสินค้า:
type Product {
id: ID!
name: String!
description(language: String = "en"): String!
}
ซึ่งช่วยให้ไคลเอ็นต์สามารถร้องขอคำอธิบายในภาษาที่ต้องการได้ หากไม่ได้ระบุภาษา จะใช้ภาษาอังกฤษ (`en`) เป็นค่าเริ่มต้น
สรุป
การออกแบบ schema ที่ขยายขนาดได้เป็นสิ่งจำเป็นสำหรับการสร้าง GraphQL API ที่แข็งแกร่งและบำรุงรักษาง่าย ซึ่งสามารถรับมือกับความต้องการของแอปพลิเคชันระดับโลกได้ โดยการปฏิบัติตามหลักการที่สรุปไว้ในบทความนี้และใช้รูปแบบการออกแบบที่เหมาะสม คุณสามารถสร้าง API ที่เข้าใจง่าย แก้ไข และขยายได้ง่าย ในขณะเดียวกันก็ให้ประสิทธิภาพและความสามารถในการขยายขนาดที่ยอดเยี่ยม อย่าลืมทำ schema ของคุณให้เป็นโมดูล สามารถประกอบได้ และเป็นนามธรรม และพิจารณาความต้องการเฉพาะของผู้ใช้ทั่วโลกของคุณ
ด้วยการนำรูปแบบเหล่านี้ไปใช้ คุณจะสามารถปลดล็อกศักยภาพสูงสุดของ GraphQL และสร้าง API ที่สามารถขับเคลื่อนแอปพลิเคชันของคุณไปอีกหลายปีข้างหน้า