GraphQL Union و Interface
14 دقیقه زمان برای خواندن این مطلب نیاز است.
فهرست مطالب
- GraphQL Union و Interface: راهنمای تخصصی برای طراحی انعطافپذیر و چندریختی در API
- اینترفیس (Interface) در GraphQL
- Union در GraphQL
- تفاوتهای کلیدی بین GraphQL Union و Interface
- مثال عملی: سیستم جستجوی یکپارچه در داناپدیا
- پیادهسازی __resolveType در Union و Interface
- بهترین روشها برای طراحی Union و Interface
- اشتباهات رایج در GraphQL Union و Interface
- اعتبارسنجی و ابزارهای مرتبط
- کاربردهای پیشرفته: ترکیب Union و Interface
- مقایسه با سیستمهای دیگر (TypeScript، Java)
- پرسشهای متداول (FAQ) درباره GraphQL Union و Interface
- ۱. آیا میتوان یک اینترفیس را پیادهسازی ناقص کرد؟
- ۲. آیا Union میتواند شامل اینترفیس باشد؟
- ۳. چگونه یک فیلد مشترک را در Union بدون اینترفیس درخواست کنیم؟
- ۴. آیا میتوانم درخواست تمام فیلدهای یک اینترفیس را بدون fragment انجام دهم؟
- ۵. چه تعداد نوع میتوان در یک Union قرار داد؟
- ۶. آیا ابزارهایی برای تولید خودکار __resolveType وجود دارد؟
- ۷. تفاوت … on Type در Union و Interface با Directiveها چیست؟
- ۸. آیا میتوانم یک Union را به عنوان ورودی (input) استفاده کنم؟
- ۹. چگونه در سمت کلاینت (React، Vue) با Unionها کار کنیم؟
- ۱۰. آیا استفاده بیش از حد از Union و Interface بر عملکرد تأثیر میگذارد؟
- جمعبندی نهایی از داناپدیا
GraphQL Union و Interface: راهنمای تخصصی برای طراحی انعطافپذیر و چندریختی در API
در دنیای مدرن توسعه APIها، GraphQL Union و Interface دو مفهوم بنیادی هستند که به توسعهدهندگان اجازه میدهند طرحهای انعطافپذیر و قابلتوسعهای ایجاد کنند. زمانی که نیاز دارید یک فیلد بتواند انواع مختلفی از اشیاء را برگرداند (مثل جستجوی ترکیبی که هم کاربر، هم محصول و هم مقاله برمیگرداند)، یا زمانی که میخواهید رفتار مشترکی بین چندین نوع تعریف کنید، GraphQL Union و Interface ابزارهای ضروری شما هستند. در این مقاله از داناپدیا، بهطور کامل و تخصصی به بررسی تفاوتها، کاربردها، پیادهسازی، نکات بهینهسازی و اشتباهات رایج در GraphQL Union و Interface خواهیم پرداخت.
چرا به Union و Interface در GraphQL نیاز داریم؟
در REST APIها، معمولاً برای بازگرداندن انواع مختلف داده، به چندین endpoint جداگانه نیاز دارید یا باید از فیلدهای اختیاری و flag استفاده کنید که خوانایی و نوع‑ایمنی را کاهش میدهد. GraphQL Union و Interface این مشکل را حل میکنند:
- Interface مجموعهای از فیلدهای مشترک را تعریف میکند که چندین نوع میتوانند آنها را پیادهسازی کنند. مشابه اینترفیسها در زبانهای برنامهنویسی شیءگرا.
- Union به یک فیلد اجازه میدهد که یکی از چندین نوع متفاوت (که لزوماً فیلد مشترکی ندارند) را برگرداند.
درک عمیق GraphQL Union و Interface به شما کمک میکند APIهای تمیزتر، نوع‑ایمنتر و قابلیت تکاملپذیرتری طراحی کنید.
Memory Management در جاوااسکریپت
اینترفیس (Interface) در GraphQL
یک اینترفیس در GraphQL مجموعهای از فیلدها را تعریف میکند که هر نوعی که این اینترفیس را پیادهسازی کند، باید حتماً آن فیلدها را داشته باشد. GraphQL Union و Interface از این جهت با هم تفاوت دارند که اینترفیسها فیلدهای مشترک را الزامی میکنند.
تعریف یک اینترفیس ساده
interface Node {
id: ID!
createdAt: String!
updatedAt: String!
}
type User implements Node {
id: ID!
createdAt: String!
updatedAt: String!
username: String!
email: String!
}
type Product implements Node {
id: ID!
createdAt: String!
updatedAt: String!
name: String!
price: Float!
}
در این مثال، هر دو نوع User و Product اینترفیس Node را پیادهسازی کردهاند. حالا میتوانیم یک کوئری بنویسیم که اشیاء مختلف را برگرداند، اما مطمئن باشیم که فیلدهای id، createdAt و updatedAt همیشه وجود دارند.
استفاده از اینترفیس در کوئری
برای دریافت فیلدهای مشترک و همچنین فیلدهای خاص هر نوع، از عبارت ... on Type (inline fragment) استفاده میکنیم:
query searchNodes {
search(term: "graphql") {
... on Node {
id
createdAt
}
... on User {
username
email
}
... on Product {
name
price
}
}
}
در GraphQL Union و Interface، اینترفیسها زمانی عالی عمل میکنند که چندین نوع، رفتار و فیلدهای مشترک معناداری دارند.

Union در GraphQL
یونیون نوعی است که نشان میدهد یک فیلد میتواند یکی از چندین نوع عینی (object type) را برگرداند. تفاوت کلیدی GraphQL Union و Interface این است که یونیون هیچ فیلد مشترکی را الزام نمیکند. انواع عضو یک یونیون میتوانند کاملاً متفاوت باشند.
تعریف یک Union
union SearchResult = User | Product | Article
type User {
id: ID!
username: String!
}
type Product {
id: ID!
name: String!
price: Float!
}
type Article {
id: ID!
title: String!
content: String!
}
حالا یک فیلد میتوانید از نوع SearchResult باشد و هر کدام از این سه نوع را برگرداند.
استفاده از Union در کوئری
در سمت کلاینت، برای دسترسی به فیلدهای خاص هر نوع باز هم از inline fragment استفاده میکنیم:
query search($q: String!) {
search(query: $q) {
__typename
... on User {
username
}
... on Product {
name
price
}
... on Article {
title
}
}
}
فیلد متا __typename به کلاینت میگوید که دقیقاً کدام نوع برگشته شده است. در GraphQL Union و Interface، __typename برای تشخیص نوع در سمت کلاینت حیاتی است.
تفاوتهای کلیدی بین GraphQL Union و Interface
| ویژگی | Interface | Union |
|---|---|---|
| فیلدهای مشترک | الزامی (همه انواع باید فیلدهای اینترفیس را داشته باشند) | ندارد (انواع میتوانند کاملاً متفاوت باشند) |
| استفاده از inline fragment | برای دسترسی به فیلدهای خاص نوع | برای دسترسی به هر فیلدی (چون فیلد مشترکی وجود ندارد) |
| قابلیت گسترش | افزودن نوع جدید باید اینترفیس را پیادهسازی کند | افزودن نوع جدید فقط کافی است به یونیون اضافه شود |
| کاربرد ایدهآل | اشتراک رفتاری و ساختاری (مثل موجودیتهای قابل شناسایی) | بازگرداندن نتایج جستجوی ناهمگن یا رویدادهای مختلف |
در GraphQL Union و Interface انتخاب بین این دو بستگی به این دارد که آیا انواع شما فیلدهای مشترک معناداری دارند یا خیر.
مثال عملی: سیستم جستجوی یکپارچه در داناپدیا
فرض کنید در داناپدیا یک سیستم جستجو داریم که هم کاربران، هم دورههای آموزشی و هم مقالات را جستجو میکند. با استفاده از GraphQL Union و Interface میتوانیم این را به زیبایی طراحی کنیم.
تعریف سمت سرور (با Apollo Server)
const typeDefs = `#graphql
interface Content {
id: ID!
title: String!
createdAt: String!
}
type User implements Content {
id: ID!
title: String! # نام کاربری به عنوان title
createdAt: String!
email: String!
role: String!
}
type Course implements Content {
id: ID!
title: String!
createdAt: String!
duration: Int!
price: Float!
}
type Article implements Content {
id: ID!
title: String!
createdAt: String!
author: String!
readTime: Int!
}
union SearchResult = User | Course | Article
type Query {
search(term: String!): [SearchResult!]!
}
`;
const resolvers = {
SearchResult: {
__resolveType(obj) {
if (obj.email) return 'User';
if (obj.duration) return 'Course';
if (obj.author) return 'Article';
return null;
}
},
Query: {
search: (_, { term }) => {
// شبیهسازی جستجو در منابع مختلف
const users = [{ id: '1', title: 'Ali', createdAt: '2024-01-01', email: 'ali@example.com', role: 'admin' }];
const courses = [{ id: '2', title: 'GraphQL Advanced', createdAt: '2024-02-01', duration: 120, price: 99.99 }];
const articles = [{ id: '3', title: 'Union vs Interface', createdAt: '2024-03-01', author: 'Sara', readTime: 8 }];
return [...users, ...courses, ...articles];
}
}
};
در سمت کلاینت، کوئری زیر را میفرستیم:
query SearchDanaPadia($term: String!) {
search(term: $term) {
__typename
... on Content {
id
title
createdAt
}
... on User {
email
role
}
... on Course {
duration
price
}
... on Article {
author
readTime
}
}
}
این مثال به خوبی قدرت GraphQL Union و Interface را نشان میدهد: فیلدهای مشترک از اینترفیس Content میآیند و فیلدهای خاص از طریق یونیون SearchResult قابل دسترسی هستند.
React Error Boundary Libraries
پیادهسازی __resolveType در Union و Interface
در GraphQL Union و Interface، سرور باید بتواند تشخیص دهد که هر شیء برگشتی متعلق به کدام نوع است. این کار توسط توابع __resolveType انجام میشود:
برای Union
const resolvers = {
SearchResult: {
__resolveType(obj) {
if (obj.username !== undefined) return 'User';
if (obj.price !== undefined) return 'Product';
if (obj.content !== undefined) return 'Article';
return null;
}
}
};
برای Interface
const resolvers = {
Content: {
__resolveType(obj) {
if (obj.email) return 'User';
if (obj.duration) return 'Course';
if (obj.author) return 'Article';
return null;
}
}
};
بدون این توابع، سرور نمیداند هر شیء را به کدام نوع نگاشت کند. در GraphQL Union و Interface، استفاده از یک فیلد تمایزدهنده (مثل kind یا بررسی وجود فیلدهای خاص) بهترین روش است.
بهترین روشها برای طراحی Union و Interface
۱. اینترفیسها را برای اشتراک واقعی طراحی کنید
فقط فیلدهایی را در اینترفیس قرار دهید که واقعاً بین همه انواع پیادهساز مشترک هستند. قرار دادن فیلدهای اختیاری در اینترفیس معمولاً نشانه طراحی ضعیف است.
۲. از Union زمانی استفاده کنید که انواع هیچ فیلد مشترکی ندارند
اگر سعی کنید یک اینترفیس با فیلدهای خیلی عمومی (مثل id و name) بسازید ولی برخی انواع واقعاً name معناداری ندارند، بهتر است سراغ Union بروید.
۳. __typename را همیشه در کوئریهای کلاینت درخواست کنید
این کار به کلاینت امکان میدهد تا بهدرستی نوع داده را تشخیص دهد و رندر شرطی انجام دهد.
۴. از inline fragment برای پرسوجوی فیلدهای خاص استفاده کنید
در GraphQL Union و Interface، اگر فیلدی فقط در یک نوع وجود دارد، حتماً آن را داخل ... on Type قرار دهید تا کوئری معتبر بماند.
۵. مستندسازی اجباری
در اسکیما، حتماً توضیح دهید که هر یونیون شامل چه نوعهایی است و هر اینترفیس چه فیلدهایی را الزام میکند.
اشتباهات رایج در GraphQL Union و Interface
۱. فراموش کردن پیادهسازی __resolveType
این خطا باعث میشود سرور نتواند شیء را به نوع مناسب نگاشت کند و خطای “Abstract type must resolve to an Object type” دریافت خواهید کرد.
۲. استفاده از Union جایی که Interface مناسبتر است
اگر چندین نوع همیشه یک فیلد مشترک دارند، آن را در اینترفیس قرار دهید تا سمت کلاینت راحتتر بتواند فیلدها را بدون fragment درخواست کند.
۳. بازگرداندن نوع اشتباه در resolver
Resolver کوئری شما باید دقیقاً یکی از انواع عضو یونیون یا پیادهساز اینترفیس را برگرداند. در غیر این صورت خطای زمان اجرا رخ میدهد.
۴. عدم مدیریت null در resolveType
اگر __resolveType نتواند نوع را تشخیص دهد، بهتر است null برگرداند (که باعث میشود فیلد نادیده گرفته شود) یا خطای واضحی ثبت کند.
اعتبارسنجی و ابزارهای مرتبط
ابزارهای متعددی برای کار با GraphQL Union و Interface وجود دارد:
- GraphQL Code Generator: بهطور خودکار تایپهای TypeScript را برای unionها و interfaceها تولید میکند.
- Apollo Client: از inline fragmentها پشتیبانی کامل دارد و میتوانید از دستور
... on Typeدر کش نرمالایز شده استفاده کنید. - GraphQL ESLint: قوانینی برای جلوگیری از استفاده نادرست از انواع انتزاعی دارد.
- GraphQL Voyager: نمایش بصری رابطه بین اینترفیسها و یونیونها در اسکیما.
در داناپدیا توصیه میکنیم حتماً از یکی از این ابزارها برای اعتبارسنجی اسکیما استفاده کنید.
کاربردهای پیشرفته: ترکیب Union و Interface
گاهی نیاز دارید یک اینترفیس داشته باشید که چندین نوع آن را پیادهسازی میکنند، و سپس یک یونیون از زیرمجموعهای از همان انواع. در GraphQL Union و Interface این کاملاً مجاز است:
interface Animal {
name: String!
age: Int!
}
type Dog implements Animal {
name: String!
age: Int!
breed: String!
}
type Cat implements Animal {
name: String!
age: Int!
furColor: String!
}
type Bird implements Animal {
name: String!
age: Int!
wingspan: Float!
}
union Pet = Dog | Cat
حالا فیلد pets میتواند فقط سگ و گربه برگرداند، در حالی که فیلد animals همه حیوانات را برمیگرداند.
مقایسه با سیستمهای دیگر (TypeScript، Java)
اگر با TypeScript آشنا باشید، GraphQL Union و Interface بسیار شبیه به union types و interfaceهای TypeScript کار میکنند. اما تفاوت کلیدی این است که در GraphQL، unionها و interfaceها در زمان اجرا (runtime) توسط resolverها و __resolveType مدیریت میشوند، در حالی که در TypeScript فقط در زمان کامپایل وجود دارند.
در زبان جاوا، اینترفیسهای GraphQL مشابه interfaceهای جاوا هستند و یونیونها معادل مفهومی “sealed class” یا “Either” در برخی کتابخانهها میباشند.

پرسشهای متداول (FAQ) درباره GraphQL Union و Interface
۱. آیا میتوان یک اینترفیس را پیادهسازی ناقص کرد؟
خیر. هر نوعی که ادعای پیادهسازی یک اینترفیس را دارد، باید تمام فیلدهای آن اینترفیس را با همان نوع و همان غیراختیاری بودن (non-null) تعریف کند. در غیر این صورت، اعتبارسنجی اسکیما با خطا مواجه میشود.
۲. آیا Union میتواند شامل اینترفیس باشد؟
بله. در GraphQL Union و Interface، یک یونیون میتواند شامل انواع عینی و همچنین اینترفیسها باشد. اما توجه داشته باشید که هنگام resolve کردن باید نوع عینی نهایی مشخص شود.
۳. چگونه یک فیلد مشترک را در Union بدون اینترفیس درخواست کنیم؟
نمیتوانید. اگر دو نوع عضو یونیون فیلد مشترکی مثل id دارند، برای دسترسی به آن باید در هر دو fragment جداگانه آن را درخواست کنید یا یک اینترفیس تعریف کنید و یونیون را بر اساس آن اینترفیس بسازید.
۴. آیا میتوانم درخواست تمام فیلدهای یک اینترفیس را بدون fragment انجام دهم؟
بله. اگر فقط فیلدهای خود اینترفیس را میخواهید (و نه فیلدهای خاص نوع)، کافی است همان فیلدها را بدون ... on Type درخواست کنید. این یکی از مزایای GraphQL Union و Interface نسبت به Union خالص است.
۵. چه تعداد نوع میتوان در یک Union قرار داد؟
هیچ محدودیت سختی وجود ندارد، اما از نظر قابلیت نگهداری، اگر یونیون شما بیش از ۱۰ نوع داشته باشد، احتمالاً طراحی اسکیما نیاز به بازنگری دارد.
۶. آیا ابزارهایی برای تولید خودکار __resolveType وجود دارد؟
بله. برخی کتابخانهها مثل graphql-tools میتوانند بر اساس وجود فیلدهای خاص، __resolveType را بهصورت خودکار تولید کنند. اما در پروژههای بزرگ، نوشتن دستی آن کنترل بیشتری به شما میدهد.
۷. تفاوت ... on Type در Union و Interface با Directiveها چیست؟
Directiveها (مثل @include و @skip) شرطی کردن فیلدها را انجام میدهند، در حالی که inline fragment در GraphQL Union و Interface برای دسترسی به فیلدهای خاص یک نوع در یک فیلد انتزاعی استفاده میشود.
۸. آیا میتوانم یک Union را به عنوان ورودی (input) استفاده کنم؟
خیر. در مشخصات GraphQL، Unionها و Interfaceها فقط میتوانند به عنوان خروجی (output type) استفاده شوند. برای ورودیها باید از input object types یا scalar استفاده کنید.
۹. چگونه در سمت کلاینت (React، Vue) با Unionها کار کنیم؟
در Apollo Client، از دستور ... on Type در کوئری استفاده میکنید و سپس در کامپوننت، بر اساس __typename رندر شرطی انجام میدهید. همچنین میتوانید از useFragment برای نوع‑ایمنی استفاده کنید.
۱۰. آیا استفاده بیش از حد از Union و Interface بر عملکرد تأثیر میگذارد؟
بله. هر inline fragment نیازمند پردازش اضافی در سمت سرور و کلاینت است. در GraphQL Union و Interface، اگر اسکیما بسیار عمیق و انتزاعی باشد، زمان تحلیل کوئری افزایش مییابد. برای APIهای پرسرعت، تعادل ایجاد کنید.
جمعبندی نهایی از داناپدیا
GraphQL Union و Interface دو ابزار قدرتمند برای ایجاد APIهای انعطافپذیر، نوع‑ایمن و قابل توسعه هستند. در این مقاله از داناپدیا، بهطور کامل بررسی کردیم که اینترفیسها چه زمانی مناسباند (وقتی اشتراک فیلدها معنادار است) و یونیونها چه زمانی (وقتی انواع کاملاً متفاوت هستند). همچنین با مثالهای عملی، پیادهسازی __resolveType، بهترین روشها، اشتباهات رایج و پاسخ به سوالات متداول آشنا شدید. اکنون شما آمادهاید تا با بهرهگیری از GraphQL Union و Interface، APIهای خود را به سطح بعدی از انعطافپذیری و کارایی ارتقا دهید.