آشنایی با GraphQL و Graphene در پایتون – مفاهیم

آمورش GraphQL و Graphene
آمورش GraphQL و Graphene
زمان مطالعه: 13 دقیقه

با سلام و صد درود، بعد مدت ها فرصت شد تا یه مطلب جدید رو داخل بلاگ منتشر کنم.

قصدم در این مطلب آشنایی اولیه با GraphQl و استفاده از آن با استفاده از Graphene در پایتون هست. طبق نوشته های قبلی بلاگ، اکثر مطالب رو با استفاده از مراجع اصلی می نویسم (ترجمه متون اصلی مستندات و همچنین یه مقدار اضافات جهت درک ساده تر مطالب) و هر جاش سوال یا نکته ای بود در کامنت ها به اشتراک بذارید تا همگی استفاده کنیم. شاید بعد از آماده شدن آموزش پیش رو، یه اموزش ویدیویی هم آماده کنم.

خب بسم الله …

اول از همه مراجع رو معرفی کنم:

1 – GraphQl چیست؟

گراف کیو ال یک زبان کوئری برای API های شما می باشد.

GraphQL برای شما روشی استاندارد جهت موارد زیر را فراهم می کند:

  • داده های ارائه شده توسط سرور را در یک تایپ ایستا به نام Schema توصیف می کند.
  • داده را تحت یک کوئری Query درخواست می کند که کامل مشخص می کند داده مورد درخواست چیست.
  • داده را تحت یک Response که فقط داده های مورد انتظار(مورد درخواست) شما را برگردانده می گیرد.

پس یه تایپ استاتیک به نام Schema داریم، درخواستمون رو با استفاده از یک کوئری به سرور ارسال می کنیم که داخل کوئری کامل مشخص می کنیم چه داده ای رو انتظار داریم و سرور هم تحت یه Response دقیق داده ای که درخواست کرده بودیم رو برمیگردونه 🙂

بد نیست برای آشنایی بیشتر با GraphQL قبل از اینکه برید سراغ Graphene یه نگاهی به خود وبسایت GraphQL بندازید و با دیدن مثال هاش با مفاهیم GraphQL آشنا خواهید شد.

2- Graphene چیست؟

گرافن یک کتابخانه برای پایتون هست که ابزارهایی رو برای پیاده سازی APIهای GraphQL با رویکرد code-first فراهم کرده است.

در مقایسه رویکرد code-first در Graphene جهت ساخت APIهای GraphQL با رویکرد های Schema-first مثل Apolo Server (برای جاوا اسکریپت) یا Aradne (برای پایتون) ، در رویکرد code-first به جای استفاده از زبان-تعریف-اسکیما (Schema Definition Language (SDL)) ، جهت توصیف داده های فراهم شده توسط سرور از کدهای پایتون استفاده می شود. (در code-first به جای استفاده از SDL از کد پایتون استفاده می شود).

گرافن به طور کامل قابلیت ادغام شدن (integration) با معروفترین فریمورک های وب و ORM ها را دارد. گرافن schema هایی را تولید می کند که به طور کامل با GraphQL مطابقت دارد و همچنین ابزارها و الگوهایی (pattern) برای ساخت یک API که به خوبی سازگار با Relay باشد ارائه می دهد.

3- یک مثال از Graphen

خب بریم که یه اسکیما پایه GraphQl با Graphene داشته باشیم که بگه Helllo و Goodbye:

زمانی که یک Query رو میفرستیم و درخواست یک Field می کنیم (در اینجا hello) و همچنین یک مقداری را برای Argument-آرگومان در نظر می گیریم (در اینجا firstname)

{
  hello(firstName: "friend")
}

انتظار داریم که جواب پایین شامل فقط داده ای که درخواست کرده ایم را داشته باشیم.

{
  "data": {
    "hello": "Hello friend!"
  }
}

پیش نیاز های

  • Python (3.6, 3.7, 3.8, 3.9, 3.10, pypy)
  • Graphene (3.0)

نصب گرافن:

pip install "graphene>=3.0"

ساخت یک اسکیمای اولیه در گرافن:

from graphene import ObjectType, String, Schema

class Query(ObjectType):
    # this defines a Field `hello` in our Schema with a single Argument `first_name`
    # By default, the argument name will automatically be camel-based into firstName in the generated schema
    hello = String(first_name=String(default_value="stranger"))
    goodbye = String()

    # our Resolver method takes the GraphQL context (root, info) as well as
    # Argument (first_name) for the Field and returns data for the query Response
    def resolve_hello(root, info, first_name):
        return f'Hello {first_name}!'

    def resolve_goodbye(root, info):
        return 'See ya!'

schema = Schema(query=Query)

در کد بالا خط ششم، فیلد hello تعریف شده که دارای یک آرگومان با نام firstname می باشد.

به صورت پیشفرض نام آرگومان به صورت camel-based یعنی firstName ذلخل اسکیمای ساخته شده قرار می گیرد.

یک اسکیمای GraphQL هر Field را در مدل داده ای فراهم شده توسط سرور با استفاده از تایپ های scalar – سنجشی همچون String، Int، Enum و انواع ترکیبی مانند List و Object توصیف می کند. برا طالاعات بیشتر در ادامه تحت عنوان انواع تایپ های گرافن به اون خواهیم پرداخت.

اسکیما همچنین می تواند هر تعداد Argument برای Field ها تعریف کند. راهی قدرتمند برای هر Query که دقیق تشریح کنه برای هر فیلد دقیقا چه داده ای را نیاز دارد.

برای هر فیلد در اسکیما، یک متد Resolver برای fetch کردن داده درخواست شده توسط کوئری کلاینت می نویسیم که از context فعلی و Argumentها استفاده می کند. برا اطلاعات بیشتر در ادامه در قسمت resolver ها به آن خواهیم پرداخت.

زبان تعریف اسکیما (SDL) چیست ؟

در GraphQL Schema Definition Language ما می توانیم فیلدهای تعریف شده در کد مثالمون رو به صورت زیر تشریح کنیم:

type Query {
  hello(firstName: String = "stranger"): String
  goodbye: String
}

نمونه مثال های بیشتر در این آموزش از SDL جهت تشریح اسکیما ساخته شده توسط ObjectType ها و دیگر فیلد ها استفاده خواهد شد تا در درک بیشتر GraphQL کمک کند.

کوئری زدن-Querying:

حال می تونیم اسکیما رو کوئری بزنیم، برای اینکار باید GraphQL query string را به execute پاس دهیم.

# we can query for our field (with the default argument)
query_string = '{ hello }'
result = schema.execute(query_string)
print(result.data['hello'])
# "Hello stranger!"

# or passing the argument in the query
query_with_argument = '{ hello(firstName: "GraphQL") }'
result = schema.execute(query_with_argument)
print(result.data['hello'])
# "Hello GraphQL!"

آشنایی با Type ها در Graphene :

تایپ Schema :

اسکیما در گراف کیو ال، تایپ ها و روابط بین فبلد ها را در API تعریف می کند.

یک اسکیما با تامین ObjectType ریشه از هر operation ، کوئری(الزامی)، mutation و subscription ایجاد می شود.

اسکیما تمامی تعاریف تایپ مرتبط با عملیات های ریشه را جکع آوری کرده و سپس آنها را به validator و executor ارائه می کند.

my_schema = Schema(
    query=MyRootQuery,
    mutation=MyRootMutation,
    subscription=MyRootSubscription
)

کوئری ریشه (root Query)، یک ObjectType ویژه است که فیلد ها که نقاط ورودی API هستند را تعریف می کند. Root Mutation و Root Subscription مشابه Root Query هستند، اما برای انواع عملیات مختلف:

  • کوئری داده ها را fetch می کند.
  • Mutatuioon داده ها را تغییر داده و تغییرات را بازیابی می کند (retrieve)
  • subscription تغییرات را به صورت real-time به سمت کلاینت ارسال می کند.

خب مثال زیر رو از سایت GraphQL آوردم و یه نگاهی بهش داشته باشیم برای آشنایی بیشتر با تایپ ها در GraphQL :

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ]
    }
  }
}
{
  hero {
    name
    appearsIn
  }
}

1 – با یک Object ویژه root کار را شروع می کنیم.

2- فیلد here را روی آن انتخاب (select) می کنیم.

3- برای Objectی که توسط here برایمان return شده است، فیلد های name و appearsIn را انتخاب (select) می کنیم.

بدیلیل اینکه شکل کوئری در GraphQL خیلی شبیه به نتیجه می باشد، شما میتوانید نتیجه ای که کوئری return می کند رو بدون اینکه اطلاعات زیادی درباره سرور بدونید، پیشبینی کنید. اما کاربردی تر اینه که توضیحات دقیق از داده ای که پرس و جو می کنیم داشته باشیم- چه فیلدی را می توانیم select کنیم؟ چه نوع (type) از object ممکن است return شود؟ چه فیلد هایی برای آن sub-object در دسترس می باشد؟، اینجاست که اسکیما-schema ئارد کار می شود.

هر سرویس GraphQL یک مجموعه از تایپ ها را تعریف می کند که به صورت کامل مجموعه داده ای که شما امکان کوئری روی آن سرویس دارید را تشریح می کنند. سپس زمانی که کوئری می رسد، آنها در برابر اسکیما اعتبارسنجی(validate) و اجرا (execute) می شوند.

زبان تایپ

سرویس های GraphQL می توانند به هر زبانی نوشته شوند. از آنجایی که برای صحبت در مورد اسکیما نمی توان به سینتکس یک زبان برنامه نویسی مشخصی مانند جاوا اسکریپت تکیه کرد، زبان ساده ای برای GraphQL تعریف شده است. در GraphQL از زبان اسکیما گرف کیو ال – “GraphQL schema language” استفاده می شود، این زبان خیلی شبیه به زبان کوئری می باشد، و به ما اجازه می دهد تا در مورد اسکیما های GraphQL به روشی زبانشناسانه صحبت کنیم.

تایپ های Object و فیلد ها

پایه ای ترین جزء از GraphQL Schema تایپ های آبجکت (object types) می باشد. که صرفا نوع آبجکتی که شما می تواند از روی سوریس fetchکنید و اینکه چه فیلد هایی دارد را مشخص می کند. در زبان اسکیما GraphQL ، ممکن است آن را به صورت زیر نمایش دهیم:

type Character {
  name: String!
  appearsIn: [Episode!]!
}

زبان ااسکیما گرف کیو ال خوانایی بسیار زیبایی دارد:) اما باز هم بیاید یه مروری روش داشته بشایم تا بتونیم واژگان مشترکی داشته باشیم:

  • Character – یک Object Type در GraphQL می باشد و به این معناست که تایپی با چندین فیلد است. بیشتر تایپ موجود در اسکیما شما، Object Type ها خواهند بود.
  • name و appearsIn فیلد هایی در تایپ Character هستند. به این معنی که name و appearsIn تنها فیلدهایی هستند که در هر قسمتی از کوئری GraphQL که روی تایپ Character عمل شود نمایش داده می شوند.
  • String از انواع تایپ های scalar داخلی می باشد – اینها تایپ هایی هستند که روی یک scalar object تنها حل می شوند و نمیتوانند داخل گوئری sub-selection داشته باشند. جلوتربه scalar type ها خواهیم پرداخت.
  • String! به این معنی است که این فیلد non-nullable می باشد. این معنی را می دهد که سرویس GraphQL به شما این قول را می دهد که همیشه زمانی که این فیلد را کوئری می کنید، مقداری را برای شما داشته باشد. پس این فیلد ها را با علامت تعجب نشان می دهیم.
  • [Episode!]! آرایه ای از آبجکت های Episode می باشد. از اون جهت که این هم به صورت non-nullable می باشد، شما همیشه باید انتظار آرایه (با صفر آیتم یا چندین آیتم) را به هنگام کوئری روی فیلد appearsIn داشته باشید. و از اونجایی که خود Episode نیز با ! (علامت تعجب) آمده، شما همیشه باید انتظار داشته باشید که هر آیتم از آرایه یک آیتم از آبجکت Episode باشد.

حالا دیگه شما می دونید که یک GraphQL Object Type شبیه به چی هست و اینکه چطور موارد پایه از GraphQL type language را بخوانید.

آرگومان ها

هر فیلد در GraphQL object type میتواند صفر یا چند آرگومان داشته باشد. برای مثال به فیلد length در مثال زیر توجه داشته باشید:

type Starship {
  id: ID!
  name: String!
  length(unit: LengthUnit = METER): Float
}

تمامی آرگومان ها نامگذاری می شوند. برخلاف زبان هایی مثل جاوا اسکریپت و پایتون، زمانی که فانکشن ها لیستی از آرگومان ها مرتب شده را می گیرند، تمامی آرگومان ها در GraphQL با نام خاص ارسال می شوند. در این مثال، فیلد length دارای یک آرگومان تعریف شده می باشد، آرگومان unit.

آرگومان می تواند ضروری (required) یا دلخواه(optional) باشد. زمانی که آرگومان آپشنال باشد، میتوان مقدار پیشفرض(Default Value) تعریف کرد – اگر آرگومان unit ارسال نشود، به صورت پیشفرض مقدار METER را می گیرد.

تایپ های کوئری و Mutation

اکثر تایپ ها در اسکیما Object type های معمولی هستند، اما دو تا تایپ خاص درون اسکیما نیز وجود دارد:

schema {
  query: Query
  mutation: Mutation
}

هر سرویس GraphQL یک تایپ کوئری (query type) دارد و ممکن mutation هم داشته باشه یا ممکن نداشته باشه. این تایپ ها مشابه با object type های معمولی هستند، اما آنها خاص هستند چراکه آنها نقطه ورود هر GraphQL را تعریف می کنند. بنابراین اگر یک کوئری مشابه زیر دیدید:

{
  "data": {
    "hero": {
      "name": "R2-D2"
    },
    "droid": {
      "name": "C-3PO"
    }
  }
}
query {
  hero {
    name
  }
  droid(id: "2000") {
    name
  }
}

به این معنی است که سرویس GraphQL نیاز دارد تا یک تایپ از نوع Query با فیلد های hero و droid داشته باشد:

type Query {
  hero(episode: Episode): Character
  droid(id: ID!): Droid
}

mutation ها به روش مشابهی کار می کنند- فیلدها را روی تایپ mutation تعریف می کنید، و این فیلدها به عنوان فیلدهای Mutation ریشه (Root mutation) در دسترس هستند که میتواند در کوئری آنها را فراخوانی کنید.

این مهمه که به یاد داشته باشید به غیر از وضعیت خاص به عنوان entry point روی اسکیما، تایپ های Query و Mutation مشابه با دیگر Object type های GraphQL هستند و فیلد هایشان کاملا مشابه کار می کنند.

تایپ های Scalar

یک Object type در GraphQL دارای نام و فیلدهایی است، اما در بعضی اوقات اون فیلد ها باید به برخی داده های concrete تبدیل شوند. اینجا جایی است که تایپ های scalar وارد می شوند: آنها برگ های کوئری را نشان می دهند.

در کوئری پیش رو، فیلدهای name و appearsIn به تایپ scalar منتج می شوند.

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ]
    }
  }
}
{
  hero {
    name
    appearsIn
  }
}

ما این را می دانیم زیرا اون فیلد ها هیچ زیر-فیلدی (sub-field) ندارند – اونها برگ های کوئری هستند.

GraphQL با مجموعه ای از تایپ های scalar پیشفرض آماده ارائه می شود:

  • Int: A signed 32‐bit integer. اینتیجر
  • Float: A signed double-precision floating-point value. فلوت – شناور
  • String: A UTF‐8 character sequence. استرینگ – متنی
  • Boolean: true or false. بولین –
  • ID: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable.

در اغلب پیاده سازی های سرویس GraphQL، همچنین روشی برای در نظر گرفتن scalar type های شخصی سازی شده نیز وجود دارد. برای مثال می توانم یه نوع Date تعریف کنیم:

scalar Date

سپس این به پیاده سازی ما بستگی داره که اون تایپ چطوری serialized شود یا deserialized شود و همچنین اعتبارسنجی شود.به عنوان مثال، می‌توانید تعیین کنید که نوع Date همیشه باید به صورت یک timestamp اینتیجر باشد و کلاینت شما باید بداند که برای هر فیلد تاریخ آن قالب را انتظار دارد.

تایپ های Enumeration

Enum نیز نامیده میشن، تایپ های Enumeration نوع خاصی از scalar هستند که محدود به مجموعه ی مشخصی از مقادیر مجاز میشوند. این به شما اجازه می دهد تا :

1- اعتبارسنجی کنید که هر آرگومان از این نوع، یکی از مقادیر مجاز می باشد.

2- از طریق type system ارتباط برقرار کنید که یک فیلد همیشه یکی از مقادیر یک مجموعه متناهی است.

در ادامه نشان می دیم که تعریف یک enum در زبان اسکیما GraphQL به چه صورت می ب

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

به این معنی است که هرجایی در اسکیما که از تایپی به نام Episode استفاده میشه، انتظار داریم دقیقا یکی از مقادیر NEWHOPE، EMPIRE یا JEDI باشد.

توجه داشته باشید که پیاده‌سازی سرویس GraphQL در زبان‌های مختلف، روش خاص خود را برای کار کردن با enumها خواهد داشت. در زبان هایی که از enums به عنوان یک شهروند درجه یک پشتیبانی می کنند، پیاده سازی ممکن است از این مزیت استفاده کند. در زبانی مانند جاوا اسکریپت بدون پشتیبانی از enum، این مقادیر ممکن است به صورت داخلی به مجموعه ای از اعداد صحیح نگاشت شوند. با این حال، این جزئیات به کلاینت درز نمی کند، که می تواند کاملاً بر اساس string name های مقادیر enum عمل کند.

لیست ها و Non-Null

Object type ها، Scalar ها و enum ها تنها انواعی از type ها هستند مه می توان در GraphQLتعریف کرد. اما زمانی که شما از تایپ ها در دیگر قسمت های اسکیما یا در اعلام متغیرهای Query استفاده می کنید، میتوانید تایپ مدیفایر های اضافی را که روی اعتبارسنجی مقادیر اون تایپ ها تاثیر می گذارند را تعریف کنید. بیاید به یه مثال نگاه بندزایم:

type Character {
  name: String!
  appearsIn: [Episode]!
}

در اینجا از تایپ String استفاده کردیم و آنرا با Non-Nul با استفاده از افزودن علامت تعجب بعد زا نام تایپ مشخص کرده ایم. به این معنی که سرور ما همیشه انتظار return کردن یه مقدار non-null برای این فیلد را دارد و اگر مقدار null بگیرد، در حقیقت یک GraphQL execution error را تریگر می کند تا به کلاینت نشان دهد که یه جار کار میلنگه 🙂

Non-Null type modifier را می توان زمانی که آرگومان را برای یک فیلد تعریف می کنیم نیز استفاده کرد که مکنجر می شود GraphQLدر صورت فرستادن مقدار null برای آن فیلد یک validation error برگرداند، همچنین در GraphQL string یا در متغیر ها.

{
  "errors": [
    {
      "message": "Variable \"$id\" of non-null type \"ID!\" must not be null.",
      "locations": [
        {
          "line": 1,
          "column": 17
        }
      ]
    }
  ]
}
query DroidById($id: ID!) {
  droid(id: $id) {
    name
  }
}
{
  "id": null
}

لیست ها نیز در روشی مشابه استفاده می شوند. میتوان از یک type modifier جهت مشخص کردن یک تایپ به عنوان لیست استفاده کرد که نشان می دهد این فیلد لیست از تایپ ها را returnخواهد کرد. در زبان اسکسما، این مورد با قرار دادن تایپ درون براکت مربع، [ و ] نشان داده می شود. برای آرگومان‌ها به همین صورت عمل می‌کند، که در مرحله اعتبارسنجی انتظار آرایه‌ای برای آن مقدار دارد.

Non-Null و لیست را میتوان باهم ترکیب کرد. برای مثال میتوانید لیستی از String های Non-Null را داشته باشید:

myField: [String!]

به این معنی است که خود لیست میتونه null باشه ولی نمیتونه هیچ عضو null ای را داشته باشد. برای مثال در JSON :

myField: null // valid
myField: [] // valid
myField: ["a", "b"] // valid
myField: ["a", null, "b"] // error

حال، بیاید بگیم که ما لیستی از string ها که Non-Null هست را تعریف کرده ایم:

myField: [String]!
به این معنیست که خود لیست به تنهایی نمیتونه null باشه ولی میتونه شامل مقادیر null باشه :
myField: null // error
myField: [] // valid
myField: ["a", "b"] // valid
myField: ["a", null, "b"] // valid

با توجه به نیاز خود، می‌توانید به‌طور دلخواهانه هر تعداد مدیفایر Non-Null و List را به صورت تودرتو (nest) استفاده کنید.

اینترفیس ها – Interfaces

“|؟ مشابه اکثر Type system ها ، GraphQL نیز از interface ها پیشتیبانی می کند. یک interface یک نوع انتزاعی می باشد که شامل مجموعه مشخصی از فیلدها می باشد که باید جهت پیاده سازی interface شامل شوند.

برای مثال، شما می توانید یک INTERFACE با نام Character داشته باشید که هر کارکتری در Star Wars trilogy را نشان دهد:

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}

به این معنی که هر تایپی که Character را پیاده سازی می کند نیاز دارد تا دقیقا این فیلد ها یا این آرگومان ها و return type ها را داشته باشد.

برای مثال، اینجا تعدادی تایپ هست که ممکن است Character را پیاده سازی کنند:

type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  starships: [Starship]
  totalCredits: Int
}

type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  primaryFunction: String
}

شما میتونید مشاهده کنید که هر دوتای این تایپ ها تمامی فیلد های اینترفیس Character را دارند، اما فیلدهای اضافی نیز دارند، totalCredits، starships و primaryFunction، که آنها مختص به تایپ خاصی از Character هستند.

interface زمانی کاربردی است که میخواهید یک object یا مجموعه ای از object ها ر return کنید اما آنها ممکن است دارای چندین تایپ متفاوت باشند.

برای مثال، توجه داشته باشید که کوئری پیش رو ارور تولید خو اهد کرد:

{
  "errors": [
    {
      "message": "Cannot query field \"primaryFunction\" on type \"Character\". Did you mean to use an inline fragment on \"Droid\"?",
      "locations": [
        {
          "line": 4,
          "column": 5
        }
      ]
    }
  ]
}
query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    primaryFunction
  }
}
{
  "ep": "JEDI"
}

فیلد hero تایپ Character را return می کند، به این معنی که با توجه به آرگومان episode میتونه Human باشه یا Droid . در کوئری بالا شما تنها میتونید از فیلدهای سوال کنید که در اینترفیس Character وجود داشته باشندو شامل primaryFunction نباشند.

جهت سوال کردن برای یک فیلد در یک object type خاص، شما نیاز دارید از فرگمنت خطی – inline fragment استفاده کنید:

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "primaryFunction": "Astromech"
    }
  }
}
query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    ... on Droid {
      primaryFunction
    }
  }
}
{
  "ep": "JEDI"
}

اینجا بیشتر میتونید در مورد فرگمنت خطی – inline fragment بخوانید.

Union types در GraphQL – تایپ های اتحادیه 🙂

Union type ها خیلی شبیه به interface هستند اما نمی توانند فیلدهای مشترکی را بین تایپ ها مشخص کنند.

union SearchResult = Human | Droid | Starship

هر جایی درون اسکیما که تایپ SearchResult را return کنیم، ما ممکن یه تایپ Human یا Droid یا یک Starchip داشته باشیم. توجه داشته بشایم که اعضای یک union type نیاز است تا concrete object type باشند، شما نمیتونید یک union type را بیرون از یک inrterface یا دیگر unionها ایجاد کنید.

در این مورد، اگر فیلدی را کوئری کنید که SearchResult را retrun کند (یک union type)، باید از یک inline fragment استفاده کنید تا بتونید فیلد ها را کوئری کنید:

{
  "data": {
    "search": [
      {
        "__typename": "Human",
        "name": "Han Solo",
        "height": 1.8
      },
      {
        "__typename": "Human",
        "name": "Leia Organa",
        "height": 1.5
      },
      {
        "__typename": "Starship",
        "name": "TIE Advanced x1",
        "length": 9.2
      }
    ]
  }
}
{
  search(text: "an") {
    __typename
    ... on Human {
      name
      height
    }
    ... on Droid {
      name
      primaryFunction
    }
    ... on Starship {
      name
      length
    }
  }
}

فیلد __typename به string ای تبدیل می شود که به شما این اجاز را می دهد تا انواع مختلف تایپ های داده را در کلاینت از یکدیگر متمایز کنید.
همچنین در این مورد چون Home و Droid اینترفیس مشابهی را به اشتراک میگذارند (Character)، شما میتوانید فیلد های مشترک آنها را به جای آنکه فیلدهای یکسان را در چندین تایپ کوئری کنید، در یک جا کوئری کنید.

{
  search(text: "an") {
    __typename
    ... on Character {
      name
    }
    ... on Human {
      height
    }
    ... on Droid {
      primaryFunction
    }
    ... on Starship {
      name
      length
    }
  }
}

توجه داشته باشید که name هنوز در starship مشخص شده است زیرا در غیر این صورت با توجه به اینکه Starship یک Character نیست، در نتایج نشان داده نمی شود.

تایپ های ورودی در GraphQL

تا الان فقط در مورد انتقال مقادیر scalar صحبت کردیم، مشابه enumها یا sting ها به عنوان آرگومان داخل یک فیلد. اما شما همچنین می توانید به سادگی object ها پیچیده را نیز انتقال دهید. این کار بویژه در mutation ها ارزشمند است، جایی که شما ممکن است بخواهید تحت یک object کامل انتقال دهید تا ساخته شود. در زبان اسکیما GraphQL ، تایپ های ورودی کاملا شبیه بهobject type های معمولی به نظر می رسند، اما با کلمه کلیدی input بجای type:

input ReviewInput {
  stars: Int!
  commentary: String
}

اینجا روش استفاده از object type ورودی در یک mutation نشان داده شده است:

{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}
{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}

فیلد ها روی یک Object type ورودی، خود میتوانند به انواع object type وردی اشاره کنند، اما شما نمیتوانید تایپ های ورودی ئ خروجی را درون اسکیمای خودتون ترکیب کنید. Object typeهای ورودی (input) همچنین نمی توانند روی فیلدهایشان آرگومان داشته باشند.