הבנת מערכת הסוגים של GraphQL

הסופר בחר ב־קרן קוד פתוח וחינמית לקבלת תרומה כחלק מתוכנית כתיבה בעבור תרומות.

הקדמה

GraphQL הוא פתרון מודרני לקידום תקשורת בין חזית העבודה למקור נתונים. כל הפרטים והיכולות של יישום GraphQL מוסכמים ב־סכימת GraphQL. כדי לכתוב סכימת GraphQL פועלת, עליכם להבין את מערכת הסוגים של GraphQL.

במאמר זה, תלמדו על סוגי GraphQL: הסוגים הסקלריים המובנים החמישה, האנומים, סוגי העטיפה רשימה ולא-ריק, סוגי Object, והסוגים המופשטים ממשק ו־איחוד שעובדים בצידם. תסקור דוגמאות לכל סוג ותלמד כיצד להשתמש בהם כדי לבנות סכימה של GraphQL מושלמת.

דרישות מוקדמות

כדי להשיג את המרב מהמדריך הזה, יש לך צורך ב:

סוגי סקאלר

כל הנתונים בסכימת GraphQL סופית מתמצאים בסופית סוגי סקאלר שונים, שמייצגים ערכים פרימיטיביים. תגובות GraphQL יכולות להיות מיוצגות כעץ, והסוגי סקאלר הם העלים בסופו של העץ. יכולים להיות הרבה רמות בתגובה מקוננת, אך הרמה האחרונה תמיד תפתור לסוג סקאלר (או Enum). GraphQL מגיע עם חמישה סוגי סקאלר מובנים: Int, Float, String, Boolean ו־ID.

Int

Int הוא ערך מספרי חשבוני חיובי או שלילי בגודל של 32 ביטים ללא פרקונים. זהו מספר שלם שחיובי או שלילי ואינו כולל ספרות עשרוניות. הערך המרבי של מספר שלם חתום ב-32 ביטים הוא 2,147,483,647. זהו אחד משני הסקאלרים המובנים המשמשים לנתונים מספריים.

Float

A Float is a signed double-precision fractional value. It is a signed (positive or negative) number that contains a decimal point, such as 1.2. This is the other built-in scalar used for numerical data.

String

A String is a UTF-8 character sequence. The String type is used for any textual data. This can also include data like very large numbers. Most custom scalars will be types of string data.

Boolean

A Boolean is a true or false value.

ID

ערך ID הוא זיהוי ייחודי. ערך זה תמיד מתואר כמחרוזת, גם אם ה-ID הוא מספרי. סוג ה-ID יכול להיות מיוצג באופן נפוץ בעזרת מזהה ייחודי לכל היקף (UUID).

סקלרים מותאמים אישית

בנוסף לסקלרים המובנים אלה, ניתן להשתמש במילת המפתח scalar כדי להגדיר סקלר מותאם אישית. ניתן להשתמש בסקלרים מותאמים אישית כדי ליצור סוגים שיש להם אימות ברמת השרת נוסף, כגון Date, Time, או Url. הנה דוגמה להגדרת סוג Date חדש:

scalar Date

השרת ידע לטפל באינטראקציות עם סוג זה באמצעות GraphQLScalarType.

סוג Enum

סוג ה-Enum, ידוע גם כסוג Enumerator, מתאר קבוצה של ערכים אפשריים.

באמצעות ערכת ה- API של משחקי הדמיון מהתמכרים אחרים בסדרה כיצד לנהל נתונים עם GraphQL, ניתן ליצור enum עבור תפקידי הגיבורים במשחק וסוגי היצורים עם כל הערכים שהמערכת תקבל עבורם. Enum מוגדר עם מילת המפתח enum, כך:

"The job class of the character."
enum Job {
  FIGHTER
  WIZARD
}

"The species or ancestry of the character."
enum Species {
  HUMAN
  ELF
  DWARF
}

בכך, הוא מובטח שהערך של Job של דמות הוא FIGHTER או WIZARD ולא יכול להיות בטעות "אדום" או מחרוזת אקראית אחרת, שיכולה לקרות אם היית משתמש בסוג String במקום ליצור Enum מותאם אישית. Enums כתובים באותיות גדולות לפי המסורת.

Enums יכולים גם לשמש כערכים מקובלים בארגומנטים. לדוגמה, תוכל ליצור Enum של Hand כדי לציין האם נשק הוא יד יחידה (כמו חרב קצרה) או יד כפולה (כמו אקס כבד), ולהשתמש בזה כדי לקבוע אם ניתן לצייד עם יד אחת או שתיים:

enum Hand {
  SINGLE
  DOUBLE
}

"A valiant weapon wielded by a fighter."
type Weapon {
  name: String!
  attack: Int
  range: Int
  hand: Hand
}

type Query {
  weapons(hand: Hand = SINGLE): [Weapon]
}

ה-Enum Hand הוכרז עם ערכים SINGLE ו-DOUBLE, והארגומנט בשדה weapons מכיל ברירת מחדל של SINGLE, שפירושו אם אין ארגומנט שמועבר אז הוא יחזור ל-SINGLE.

סוג Non-Null

תוכל לשים לב ש-null או undefined, סוג שנחשב בצורה נפרדת בשפות רבות כפרימיטיבי, חסר מהרשימה של סקלרים מובנים. Null קיים ב-GraphQL ומייצג אי זמינות של ערך.

הכללים ב-GraphQL מחייבים את כל הסוגים להיות null כברירת מחדל, ולכן null הוא תגובה חוקית לכל סוג. כדי להגדיר ערך כחובה, עליו להיות מומר לסוג Non-Null בעזרת סימן קריאה בסיומת. Non-Null מוגדר כמובן, אשר הם סוגי שמשמשים לשנות את הסוג שהם מתייחסים אליו. לדוגמה, String הוא מחרוזת אופציונלית (או מקובלת), ו-String! הוא מחרוזת דרושה (או לא מקובלת).

סוג רשימה

A List type in GraphQL is another type modifier. Any type that is wrapped in square brackets ([]) becomes a List type, which is a collection that defines the type of each item in a list.

לדוגמה, סוג שמוגדר כ-[Int] יהיה אוסף של סוגי Int, ו-[String] יהיה אוסף של סוגי String. ניתן להשתמש ביחד ב-Non-Null וברשימה כדי להגדיר סוג דרוש ומוגדר כרשימה, כמו לדוגמה [String]!.

סוג אובייקט

אם סוגי ה-Scalar של GraphQL מתארים את "העלים" בקצה התגובה ההיררכית של GraphQL, אז סוגי Object מתארים את "הסניפים" האמצעיים, וכמעט כל דבר בסכמת GraphQL הוא סוג של Object.

עצמים מורכבים מרשימת שדות מזוהים (מפתחות) וסוג הערך שכל שדה יתקבל אליו. עצמים מוגדרים עם המילה המפתח type. לפחות שדה אחד או יותר חייב להיות מוגדר, ושדות לא יכולים להתחיל בשני קווי תחתות (__) כדי למנוע התנגשות עם מערכת האינטרוספקציה של GraphQL.

בדוגמת ממשק ה- API למשחקי פנטזיה ב-GraphQL, ניתן ליצור עצם Fighter כדי לייצג סוג של דמות במשחק:

"A hero with direct combat ability and strength."
type Fighter {
  id: ID!
  name: String!
  level: Int
  active: Boolean!
}

בדוגמה זו, סוג העצם Fighter הוגדר, ויש בו ארבעה שדות מזוהים:

  • id מחזיר סוג ID לא ריק.
  • name מחזיר סוג String לא ריק.
  • level מחזיר סוג Int.
  • active מחזיר סוג Boolean לא ריק.

מעל להגדרה, ניתן גם להוסיף הערה באמצעות גרשיים כפולים, כפי שניתן לראות בדוגמה זו: "גיבור עם יכולת קרב ישירה וכוח.". זה יופיע כתיאור לסוג.

בדוגמה זו, כל שדה מסתיים בסוג סקלרי, אך שדות עצם יכולים גם להסתיים בסוגים עצמיים אחרים. לדוגמה, ניתן ליצור סוג Weapon, ולהגדיר את הסכמת ה-GraphQL כך שהשדה weapon ב-Fighter יפתור לעצם Weapon:

"A valiant weapon wielded by a fighter."
type Weapon {
  name: String!
  attack: Int
  range: Int
}

"A hero with direct combat ability and strength."
type Fighter {
  id: ID!
  name: String!
  level: Int
  active: Boolean!
  weapon: Weapon
}

עצמים יכולים גם להיות מקוננים בתוך השדות של עצמים אחרים.

סוגי פעולות השורש

קיימים שלושה אובייקטים מיוחדים המשמשים כנקודות כניסה לסכימת GraphQL: שאילתה, מוטציה ומינוי. אלה ידועים כסוגי פעולות השורש ועוקבים אחר כללי ההתנהגות של כל סוג אובייקט אחר.

המילה המפתח schema מייצגת את נקודת הכניסה לסכימת GraphQL. הסוגים הראשיים של השאילתה, המוטציה והמינוי יהיו על אובייקט השורש schema:

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

הסוג של השאילתה דרוש בכל סכימת GraphQL ומייצג בקשת קריאה, דומה לGET ב-REST API. הנה דוגמה לאובייקט שאילתתי ראשי שמחזיר רשימה של סוגי Fighter:

type Query {
  fighters: [Fighter]
}

המוטציות מייצגות בקשת כתיבה, שתהיה דומה לPOST, PUT, או DELETE ב-REST API. בדוגמה הבאה, הMutation מכיל שדה addFighter עם ארגומנט מזוהה (input):

type Mutation {
  addFighter(input: FighterInput): Fighter
}

לבסוף, מינוי מתאים לזרם אירועים, שייתכן וישמש בשילוב עם Websocket באפליקציה רשתית. ב-GraphQL Fantasy API, אולי יהיה ניתן להשתמש בו לפגישות קרב אקראיות, כמו כן:

type Subscription {
  randomBattle(enemy: Enemy): BattleResult
}

שימו לב שנקודת הכניסה schema לעיתים קרובות מוסתרת במימושים מסוימים של GraphQL.

ארגומנטים של שדה

שדות של אובייקט GraphQL בעצם הם פונקציות שמחזירות ערך, והן יכולות לקבל ארגומנטים דומים לכל פונקציה. ארגומנטי שדה מוגדרים על ידי שם הארגומנט ואחריו סוג הארגומנט. ארגומנטים יכולים להיות מכל סוג לא-אובייקט. בדוגמה זו, אובייקט ה־Fighter יכול להיות מסוננת על ידי השדה id (שמתקבלת כסוג ID לא מאולם):

type Query {
  fighter(id: ID!): Fighter
}

דוגמה זו מסוימת שימושית לקבלת פריט יחיד ממאגר הנתונים, אך ארגומנטים יכולים גם לשמש למסננים, פגינציה, ושאילתות ספציפיות נוספות.

סוג ממשק

כמו סוג האובייקט, הממשק האבסטרקטי סוג הממשק מורכב מרשימת שדות מקובעות והערכים המשויכים אליהם. ממשקים נראים ועוקבים אחר כללי המשחק כמו אובייקטים, אך משמשים להגדרת תת-מימוש של אובייקט.

עד כה בסכמתך, יש לך אובייקט Fighter, אך ייתכן גם שתרצה ליצור Wizard, Healer, ואובייקטים אחרים שיש להם מספר שדות דומים אך עם כמה הבדלים. במקרה זה, ניתן להשתמש בממשק כדי להגדיר את השדות שמשותפים לכולם, וליצור אובייקטים שהם מימושים של הממשק.

בדוגמה הבאה, ניתן ליצור ממשק BaseCharacter בעזרת המילה השמורה interface עם כל השדות שכל סוג של דמות יהיה ברשותה:

"A hero on a quest."
interface BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job
}

לכל סוג דמות תהיה ברשותה את השדות id, name, level, species, ו־job.

עכשיו, נניח שיש לך סוג Fighter וסוג Wizard שיש להם שדות משותפים אלו, אך Fighters משתמשים ב־Weapon ו־Wizards משתמשים ב־Spells. ניתן להשתמש במילת המפתח implements כדי להבחין בכל סוג כמימוש של BaseCharacter, מה שאומר שהם חייבים להכיל את כל השדות מהממשק שנוצר:

"A hero with direct combat ability and strength."
type Fighter implements BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job!
  weapon: Weapon
}

"A hero with a variety of magical powers."
type Wizard implements BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job!
  spells: [Spell]
}

Fighter ו־Wizard הם שניהם מימושים תקפים של הממשק BaseCharacter מכיוון שהם מכילים את תת־הקבוצה הדרושה של השדות.

Union Type

סוג אבסטרקט נוסף שניתן להשתמש בו עם אובייקטים הוא סוג האיחוד (Union type). באמצעות מילת המפתח union, ניתן להגדיר סוג המכיל רשימה של אובייקטים הכולם יכולים לשמש כתגובה תקפה.

על ידי שימוש בממשקים שנוצרו בקטע הקודם, ניתן ליצור סוג איחוד בשם Character שמגדיר דמות כסוג Wizard או Fighter:

union Character = Wizard | Fighter

התו השווה (=) מגדיר את ההגדרה, והתו הקו-שני (|) פועל כהצהרה לאוֹר. שימו לב כי סוג איחוד חייב להכיל אובייקטים או ממשקים. סוגי סקאלר אינם תקפים על סוג איחוד.

כעת, אם תבקש רשימת דמויות, ניתן יהיה להשתמש בסוג האיחוד Character ולהחזיר את כל סוגי הדמויות Wizard ו-Fighter.

סיכום

במדריך זה, למדת על רבים מהסוגים שמגדירים את מערכת הסוגים של GraphQL. הסוגים הבסיסיים ביותר הם הסוגים הסקלריים, שהם הערכים שמשמשים כעלים בעץ הסכמה, ומורכבים מ Int, Float, String, Boolean, ID, וכל סוג סקלרי מותאם אישית שמימוש GraphQL יחליט ליצור. Enumerations הם רשימות של ערכי קבוע תקפים שניתן להשתמש בהם כאשר נדרשת יותר שליטה על תגובה מאשר פשוט להכריז על זה כ String, והם גם עלים בעץ הסכמה. סוגים של רשימות ולא-ריקים מוכרים כסוגי מודיפיקציה, או סוגי עטיפה, והם יכולים להגדיר סוגים אחרים כאוספים או נדרשים, בהתאמה. אובייקטים הם הסניפים של עץ הסכמה, וכמעט כל מה שבעץ הסכמה של GraphQL הוא סוג של אובייקט, כולל את הנקודות הכניסה query, mutation, ו subscription. סוגים של ממשק ואיחוד הם סוגים אבסטרקטיים שיכולים להיות מועילים בהגדרת אובייקטים.

למידה נוספת, תוכל לתרגל יצירה ושינוי של סכמת GraphQL על ידי קריאה במדריך איך להגדיר שרת API של GraphQL ב Node.js כדי לקבל סביבת שרת GraphQL פועלת.

Source:
https://www.digitalocean.com/community/conceptual-articles/understanding-the-graphql-type-system