اگر شما هم برنامه نویس وب یا موبایل باشید شاید برای شما پیش آمده باشد که از یک دیتای JSON طولانی فقط نام و آدرس عکس پروفایل را لازم داشته باشید و به بقیه داده ها نیاز نداشته باشید ولی مجبورید کل دیتای یک آبجکت Json را دریافت کنید و فقط همان دو فیلد را استفاده کنید و بقیه را دور بریزید. حالت دیگری که رایج است و ممکن است که برای شما پیش آمده باشد این است که برای نشان دادن پروفایل کاربر و 3 پست آخرش و آخرین نظراتش 3 یا 4 نوع API جداگانه را فراخوانی کنید.
به این دو مشکل به ترتیب over-fetching (دریافت داده های اضافی) و Under-fetching (دریافت داده های کمتر از نیاز) می گویند که سال هاست مشکل برنامه نویسان است. این مشکل زمانی خود را بیشتر نشان می دهد که برنامه نویس موبایل باشید و بخواهید با اینترنت ضعیف کار کنید.
در سال 2012 فیسبوک یک راه حل ارائه کرد که بعدا در سال 2015 عمومی شد. این راه حل به صورت هوشمندانه تری داده ها را لود می کند که GraphQL نام دارد. در این مقاله به سادگی در مورد GraphQL توضیح خواهیم داد و نحوه کار آن را توضیح می دهیم و می گوییم که چطوری مشکلات گفته شده را حل کرده است.
GraphQL چیست؟
اسم GraphQL ما را به یاد SQL می اندازد ولی اشتباه نکنید ربطی به دیتابیس و SQL ندارد. البته زبان برنامه نویسی و تکنولوژی برنامه نویسی هم نیست. GraphQL یک زبان پرس و جو (Query language) برای API ها است. یعنی شما می توانید از داخل داده های API کوئری بگیرید و داده های مورد نیاز خود را فقط دریافت کنید. برای مثال اگر Api های Rest را منوی رستوران در نظر بگیریم GraphQL مثل میز سلف سرویس است که هرچقدر که لازم دارید از غذاها در بشقاب خود بریزید. یعنی دقیقا همان چیزی را که لازم دارید دریافت می کنید نه کمتر نه بیشتر.
GraphQL سه بخش اصلی دارد:
- Schema (طرح): مانند قراردادی است که سرور در اختیار کلاینت قرار می دهد مثل همان منوی رستوران مثل اطلاعات کاربر، پست ها ، کامنت ها
- Query(پرس و جو): مانند سفارش در رستوران است یعنی همان داده هایی است که برنامه کلاینت به آنها نیاز دارد. مثل نام کاربری و آدرس عکس پروفایل کاربر
- Resolver: این بخش در سمت سرور مسئول پردازش کوئری کاربر است. وقتی که یک کوئری برای سرور ارسال می شود این resolver است که داده ها را از منابع اطلاعاتی مختلف جمع آوری می کند و نتیجه را تولید می کند.
قبل از این که در مورد GraphQL صحبت کنیم اول ببینیم که چه لزومی دارد که از GraphQL استفاده شود و Rest API چه مشکلی ایجاد می کند.
چرا Rest API برای کار با داده های سرور کافی نیست؟
در دنیای REST به هر آدرس URL که از طریق آن API ها را فراخوانی می کنیم و داده ها را دریافت می کنیم endpoint گفته می شود. خیلی وقت ها طراحی اندپوینت ها مناسب نیست و ممکن است که دچار مشکلات Over/Under fetching شویم.
Over-fetching (دریافت بیش از نیاز داده ها)
این مشکل زمانی پیش می آید که کلاینت درخواستی به سرور ارسال می کند تا یک بخش کوچک از داده ها را دریافت کند اما سرور یک آبجکت بزرگ از داده ها را در پاسخ برای کلاینت ارسال می کند که کلاینت اصلا نیازی به اکثر آن داده ها ندارد و مثلا 95 درصد آن را دور میریزد. در این مشکل داده های زیادی منتقل می شود که می توان آن را کاهش داد. به طور مثال کلاینت یک درخواست برای سرور ارسال می کند تا فقط اسم و عکس پروفایل کاربر را نشان دهد. در روش REST مثلا یک درخواست به آدرس api/users/32 ارسال می شود تا کاربر با آیدی 32 دریافت شود. این اندپوینت هروقت که نیاز به اطلاعات کاربری باشد فراخوانی می شود. مثلا هنگامی که می خواهیم اطلاعات کاربر را ویرایش کنیم از همین اندپوینت برای دریافت اطلاعات کاربر استفاده می کنیم. سرور در پاسخ به درخواست از طریق این اندپوینت داده ای شبیه به داده های زیر را برای کلاینت ارسال می کند.
{
"id": 1,
"name": "مهدی عادلی",
"username": "mehdi_adeli",
"avatar": "url/to/image.png",
"email": "mehdi@tosinso.com",
"join_date": "2023-01-10T10:00:00Z",
"last_login": "2025-11-02T07:30:00Z",
"bio": "توسعهدهنده نرمافزار...",
"permissions": ["admin", "editor"],
"address": { ... }
// و ۴۰ تا فیلد دیگه...
}
در این درخواست فقط ما به فیلد های name, avatar نیاز داشتیم اما کلی دیتای اضافی هم دریافت کردیم. این باعث کند شدن برنامه و هدر رفتن پهنای باند شبکه خواهد شد مخصوصا روی اینترنت و دستگاه موبایل. حال اگر سرور فقط نام و آواتار را ارسال می کرد دیگر این مشکل وجود نداشت. اگر شما فقط REST بلد باشید اولین راه حلی که به ذهن شما می رسد این است که خب برای این یک اندپوینت جدید در نظر میگیریم و فقط همین داده های لازم را ارسال می کنیم. باید بگویم که بله این راه درست است ولی وقتی که سایت بسیار بزرگ شود تعداد این اندپوینت ها خیلی بالا می رود و مدیریت آنها سخت خواهد شد.
Under-fetching: دریافت کمتر از نیاز داده ها
این مشکل زمانی اتفاق می افتد که داده های کمتر از نیازمان را دریافت کنیم. در این صورت برای دریافت بقیه داده ها مجبوریم یک درخواست از یک اندپوینت دیگر را نیز ارسال کنیم. یعنی یک اندپوینت تمام اطلاعاتی را که لازم داریم را به ما نمی دهد و مجبوریم برای تکمیل داده های موجود در یک صفحه چندبار درخواست ارسال کنیم. مثلا کاربر در نظر دارد که صفحه مربوط به پست را ببیند. در این صورت باید یک درخواست برای دریافت پست به آدرس فرضی api/posts/325 ارسال شود و محتوای پست دریافت شود. همچنین باید کامنت های آن پست هم در زیر پست نمایش داده شوند پس باید یک درخواست جدید هم برای دریافت کامنت هایی که برای پست گذاشته شده به آدرس فرضی api/posts/325/comments ارسال شود از طرفی باید نام نویسنده پست راه به صورت لینک در صفحه قرار دهیم که کاربر وقتی روی آن کلیک کرد به پروفایل کاربر نویسنده پست منتقل شود. می بینید که برای نمایش یک صفحه پست ما حداقل 3 بار درخواست ارسال کرده ایم به صورت رفت و برگشتی و آبشاری، که این کار برنامه را به شدت کند می کند و تجربه بدی به کاربر می دهد.
GraphQL با این ایده مشکل را حل می کند که کلاینت دقیقا بگوید چه اطلاعاتی را لازم دارد و همان اطلاعات از سمت سرور آماده و برای کلاینت ارسال شود. GraphQL به این شکل کار می کند که یک اندپوینت مثل graphql/ دارد و به این آدرس کوئری های مختلف ارسال می شوند و نتیجه می گیرند.
برای حل مشکل over-fetching شما دقیقا بیان می کنید که چه چیزی را لازم دارید مثل کوئری زیر
query {
user(id: "16") {
name
avatar
}
}
حال جوابی که سرور برای شما ارسال می کند به شکل زیر خواهد بود
{
"data": {
"user": {
"name": "مهدی عادلی",
"avatar": "url/to/image.png"
}
}
}
می بینیم که دیگر خبری از داده های اضافه نیست و فقط نام و آواتار ارسال شده است. این کار به حفظ پهنای باند بسیار کمک می کند.
همچنین برای حل مشکل Under-fetching ما می توانیم داده های تودرتو را در یک درخواست بگیریم و با یک درخواست کار را تمام کنیم. مثلا برای مثال نمایش صفحه پست می توانیم از کوئری زیر استفاده کنیم.
query {
post(id: "325") {
title
content
author {
name
avatar
}
comments {
body
author {
name
}
}
}
}
و پاسخی که از سمت سرور در قالب json دریافت خواهد شد به شکل زیر است:
{
"data": {
"post": {
"title": "GraphQL شگفتانگیزه!",
"content": "متن کامل پست اینجا...",
"author": {
"name": "مهدی عادلی",
"avatar": "url/to/image.png"
},
"comments": [
{ "body": "عالی بود!", "author": { "name": "سارا" } },
{ "body": "موافقم.", "author": { "name": "امید" } }
]
}
}
}
دیگر نیازی به ارسال پینگ پنگی و آبشاری درخواست ها و دریافت آنها نیست و فقط یک درخواست ارسال خواهد شد.
یک بار که بر روی یک اپلیکیشن خبری کار می کردیم ابتدا پروژه را با REST پیاده سازی کرده بودیم که به دلیل ارسال و دریافت درخواست های ریز و درست برنامه بسیار کند بود و گاهی حدود 10 ثانیه طول می کشید که صفحه اصلی برنامه لود شود ولی بعد از پیاده سازی با graphql این زمان به حدود 2 ثانیه رسید.
معایب GraphQL
حال که با GraphQL آشنا شدیم آیا باید کلا REST را کنار گذاشته و به GraphQL روی بیاوریم؟ با این که GraphQL کاربرد بسیاری دارد و مزایای زیادی دارد ولی نمی توان به صورت کامل از Rest به آن مهاجرت کرد. زیرا که درست است که GraphQL قدرت خیلی دارد ولی پیچیدگی های خاص خودش را دارد که در زیر چند مورد را آورده ایم.
- پیچیدگی سمت سرور: راه اندازی یک سرور GraphQL (نوشتن schema, resolver) بسیار پیچیده تر از ساخت API های REST است.
- کش کردن (Caching). در REST API شما می توانید پاسخ های یک اندپوینت را کش کنید مثلا api/user/32 را کش کنید. اما در GraphQL به خاطر این که فقط یک اندپوینت وجود دارد و نتایج مختلف می دهد نمی توان آن را کش کرد و کش کردن آن پیچیده خواهد بود.
- کوئری های پیچیده: یکی از مشکلاتی که در GraphQL وجود دارد این است که کلاینت ها باید کوئری ها را بهینه بنویسند و اگر مراقب نباشیم کلاینت ها می توانند کوئری های پیچیده ای بسازند که فشار زیادی به سرور و دیتابیس بیاورد.
جمع بندی
GraphQL یک ابزار قدرتمند در جعبه ابزار برنامه نویس هاست که باید بدانند در کجا از آن استفاده کنند تا مفید باشد. همچنین GraphQL قاتل REST نیست بلکه یک راه حل مدرن و متفاوت برای حل مشکلات Over/Under fetching است. به صورت کلی GraphQL برای برنامه های پیچیده ، اپ های موبایل که به پهنای باند حساس هستند و سیستم هایی که منابع داده ای متعدد دارند مثل microservice ها مفید است. اما برای یک برنامه ساده یا یک وبلاگ ساده شاید استفاده از آن مناسب نباشد و بهتر باشد از REST استفاده شود.
نظرات کاربران (0)