GraphQL برای توسعه دهندگان فرانت اند – راهنمای کاربردی
GraphQL به عنوان یکی از فناوریهایی که نقشی بیبدیل در شیوه ساخت سرورهای فیسبوک و حتی شیوه کدنویسی مهندسان این شرکت داشته است، موجب تحول زیادی در طرز کار محاسبات شده است. اکوسیستم GraphQL در طی سالهای اخیر با سرعت بسیار زیادی رشد کرده است. این رشد در تجربیات ما به عنوان توسعهدهندگان روزمره نیز بازتاب یافته است.
GraphQL از سوی طیف وسیعی از تیمهای فنی بینالمللی در شرکتهایی مانند پینترست، توییتر، Yelp، نیویورکتایمز، پی پل، Atlassian، فیسبوک، گیتهاب و غیره مورد استفاده قرار میگیرد.
در این نوشته قصد داریم یک راهنمای مقدماتی در مورد GraphQL ارائه کنیم تا برنامهنویسان فرانتاند که علاقهمند به یادگیری این زبان هستند مورد استفاده قرار دهند. این راهنما به چند بخش تقسیم شده است تا کسانی که دوست دارند تنها یک بخش آن را مطالعه کنند، بتوانند از بخشهای دیگر عبور کنند:
- بخش اول: مبانی اولیه یادگیری GraphQL
- بخش دوم: GraphQL دقیقاً چیست؟
- بخش سوم: GraphQL قرار است چه مشکلی را حل کند؟
- بخش چهارم: برنامه Hello World در GraphQL
- بخش پنجم: مفاهیم GraphQL باید با چه ترتیبی آموخته شوند؟
بخش اول: مبانی مقدماتی یادگیری GraphQL
سناریوی زیر را تصور کنید. یک برنامهنویس مبتدی فرانتاند به تازگی با react آشنا شده است. وی احتمالاً با Backbone و AngularJS نیز کمی کار کرده است، اما توجهش را جلب نکردهاند.
این برنامهنویس مبتدی به شدت تحت تأثیر رویکرد تابعی React برای مدیریت حالت و استفاده از DOM مجازی قرار میگیرد تا سربار عملکردی دستکاری شدید DOM را کاهش دهد. به این منظور باید برخی تابعهای کمکی برای اجرای این کار نوشته شوند و شاید به این نتیجه برسد که بهتر است از ()React.createClass و ()React.createElement استفاده کند.
اما اگر این برنامهنویس ما بخواهد از تجربیات یک عضو ارشد تیم برنامهنویسی در این زمینه کمک بگیرد، احتمالاً مورد تمسخر و دیدگاه منفی بکاندکارها نسبت به فریمورکهای جاوا اسکریپت قرار میگیرد.
کلید حل این مشکل در چیزی به نام Gatsby است. Gatsby از GraphQL برای کوئری زدن به محتوای فرانت و محتوای متنی در فایلهای Markdown استفاده میکند. بنابراین به عنوان یک برنامهنویس فرانتاند این همان جایی است که با کاربرد GraphQL مواجه میشویم.
بخش دوم: GraphQL دقیقاً چیست؟
آلن جانسون در مقالهای تحت عنوان «?Is GraphQL The Future» (+) بیان میکند:
من آینده را دیدهام و تا حدود زیادی مانند GraphQL است. قول میدهم در طی پنج سال آینده توسعهدهندگان فولاستک اپلیکیشن دیگر از رویکرد RESTful استفاده نمیکنند زیرا طراحی REST API منسوخ خواهد شد. این رویکرد امکان مدلسازی منابع و پردازشهای ارائه شده از سوی یک سرور را در یک «زبان با دامنه خاص» (DSL) فراهم میسازد. کلاینتها میتوانند از آن برای ارسال اسکریپتهای نوشته شده در DSL شما به سرور و پردازش و سپس بازگشت دستهای پاسخها استفاده کنند.
شاید نقل قول فوق چیزی نباشد که در پاسخ به معنی دقیق GraphQL انتظار داشتید، دریافت کنید. در ادامه توضیح بیشتری میدهیم.
منظور از زبان با دامنه خاص چیست؟
برای درک دقیق تعاریف پیچیده مطرح شده در گفته آلن جانسون لازم است که برخی اصطلاحها را بیشتر باز کنیم. این کار را با توضیح مفهوم «زبان با دامنه خاص» (DSL) آغاز میکنیم.
اول باید بگوییم که یک زبان با دامنه خاص (که زبان کوچک نیز نامیده میشود)، به یک زبان برنامهنویسی اطلاق میشود که برای بیان یک نوع خاص و از پیش تعریف شده از اطلاعات دیجیتال (همان دامنه) استفاده میشود. این وضعیت مخالف شرایط زبانهای چندمنظوره مانند جاوا اسکریپت است که میتوانند برای بیان طیف وسیعی از اطلاعت دیجیتال مورد استفاده قرار گیرند. این طیف وسیع در پارهای اوقات شامل مواردی میشود که حتی خود خالقان زبان نیز آن را پیشبینی نمیکردند. این موارد شامل هر چیزی از انواع مقدماتی سطح پایین مانند اشیا، تابعها، رشتهها، نمادها تا الگوهای برنامهنویسی عمومی مانند درخواستهای HTTP، دستکاری های DOM و/یا ذخیره آنلاین دادهها میشود. زبانهای با دامنه خاص عموماً محدود هستند و این مسئله عامدانه است چون بدین ترتیب میتوانند چیزی را که میخواهند به روشی مؤثرتر از زبانهای چندمنظوره ابراز کنند.
نکته دوم این است که زبانهای با دامنه خاص در اغلب موارد به زبانهای با دامنه خاص دیگر یا زبانهای چندمنظوره وصل میشوند تا سوار کارکردهای موجود شوند. با این وجود این بدان معنی نیست که زبانهای با دامنه خاص به زبانهای خاصی وابسته هستند. GraphQL مثالی از این وضعیت است. برای نمونه زبان با دامنه خاصی به نام XForms میتواند درون HTML مورد استفاده قرار گیرد، در حالی که همزمان در زبانهای چندمنظورهای مانند جاوا نیز مورد استفاده قرار میگیرد.
آیا زبان دامنه خاص بیش از حد تخصصی نیست؟
شاید شما در واقع بیش از آن چه که فکر میکنید با زبانهای دامنه خاص کار کرده باشید. این زبانها نه تنها شامل HTML بلکه شامل موارد لیست زیر میشوند:
- CSS
- JSON
- YAML
- XML
به علاوه احتمالاً هم اینک تجربه دست اولی از دامنه کوچک آنها دارید. اغلب افراد در صورتی که تلاش کنید پایگاه دادهای را با HTML مدیریت کنید یا بخواهید یک وسیله الکترونیکی را از طریق CSS تحت کنترل در بیاورید شما را مسخره میکنند.
ارزش واقعی زبان با دامنه خاص
زبانهای با دامنه خاص به دلیل تخصصی بودنشان در قیاس با زبانهای چندمنظوره بسیار گویا هستند، یعنی خواندن و نوشتن آنها آسان است.
برای نمونه فردی به نام Greg Kempe از یک زبان به نام Akoma Ntoso که بر مبنای XML ساخته شده است در یک پروژه غیرانتفاعی به نام Open By-laws استفاده میکند. این زبان با دامنه خاص به طور اختصاص برای بیان اسناد پارلمانی، قانونی و قضایی به روش دیجیتال استفاده میشود.
برای نمونه در کد زیر بخشی از آییننامه بخش عمومی شهر کیپتاون را میبینید که به زبان Akoma Ntoso بیان شده است:
پشتیبانی از توسعه فرانتاند
برای نشان دادن نقش مفهوم فوق در توسعه فرانتاند میتوانیم به مثال رایجی که در آن مدل شیء سند (DOM) وبسایت را از طریق جاوا اسکریپت بهروز میکنیم نگاه کنیم:
جاوا اسکریپت با وجود ساختار ES6 فوق کاملاً قدرتمند است، اما زمانی که با DOM کار میکنید چندان گویا نیست. خوشبختانه یک زبان با دامنه خاص وجود دارد که برای بیان بهتر گرههای DOM مرورگر استفاده میشود. احتمالاً با HTML که اختصاری برای عبارت «زبان نشانهگذاری ابرمتن» (Hypertext Markup Language) نام دارد، آشنا هستید.
این بدان معنی است که با استفاده از مشخصه innerHTML میتوان کد فوق را بازنویسی کرد. مشخصه innerHTML یک رشته میپذیرد که در زبان با دامنه خاص HTML نوشته شده است:
این کد رفتاری دقیقاً همانند رفتار کد فوق دارد. اینک پیش از آن که به تعریف زبان با دامنه خاص GraphQL بپردازیم، بهتر است یک تمایز بین زبان با دامنه خاص و مجموعه مادر آن یعنی TypeScript یا Sass داشته باشیم. با این که یک زبان ابر مجموعه به معنی بسط گرامر زبان موجود است اما DSL نیازی به چسبیدن به هیچ زبان یا محیط خاص را ندارد. برای نمونه JSX (که بر مبنای XML ساخته شده است) میتواند برای اینترفیس کردن مستقیم DOM مرورگر یا یک سیستم عامل موبایل به شکل اپلیکیشن موبایل مورد استفاده قرار گیرد. این حالت دوم چیزی است که در React Native مشاهده میکنیم.
گرچه این تمییز بین زبانهای DSL و چندمنظوره و یا DSL با زبانهای ابر مجموعه در موارد زیادی حالت فازی دارد، اما در هر حال یک معیار و شاخص محسوب میشود.
بخش سوم: GraphQL قرار است چه مشکلی را حل کند؟
GraphQL همانند زبانهای DSL که در بخش فوق اشاره کردیم، در ابتدا از سوی یک تیم فنی داخل فیسبوک در سال 2012 به عنوان یک زبان با دامنه خاص جهت نوشتن کوئریهای دادهای گویا (و قوی) تر در اپلیکیشن موبایل فیسبوک پدید آمد.
مشکل این بود که اغلب REST API ها عموماً بر ساختمانهای داده ثابت تکیه دارند. این بدان معنی است که پس از تکرار تعداد معینی از درخواستها، اغلب REST API ها برای به دست آوردن قطعه مشخصی از دادهها نیازمند تعداد خیلی زیادی از کوئری هستند.
در هر حال GraphQL سه سال پس از تولد، به طور عمومی تحت لایسنس MIT منتشر شد و امروزه ستون فقرات سرویسهای متن-بازی مانند Apollo که از GraphQL برای خواندن/نوشتن حالتهای لوکال و ریموت اپلیکیشن استفاده میکند و حتی Gatsby که از GraphQL برای کوئری به محتوای فرانت و محتوای متن فایلهای markdown استفاده میکند تبدیل شده است.
در ادامه به بررسی یک مثال واقعی میپردازیم که روش حل مشکل کوئری دادههای با ساختمان ثابت را از سوی GraphQL نشان میدهد.
فرض کنید میخواهیم تعداد افرادی که در گیتهاب ما را فالو میکنند را بدانیم. میتوانیم بهسادگی دادهها را از طریق متد بومی جاوا اسکریپت یعنی fetch به دست بیاوریم و سپس لیستی از اسامی کاربری را در DOM از طریق یک لیست نامرتب نمایش دهیم. این کار از طریق مثال innerHTML فوق ممکن است:
زمانی که کوئریهای داده افزایش مییابند، همچنان در محدوده قابل قبول ماندهاند. با این وجود همه فراخوانیهای REST API مورد نیاز (که به طور موازی اجرا میشوند) موجب میشوند که خوانایی و اصلاح کد به شدت دشوار شود. این بدان معنی است که حتی اگر بخواهیم اطلاعات لازم را از 10 فالوور اول بگیریم، نیازمند 31 فراخوانی REST API خواهیم بود. معنی دیگر آن این است که به سرعت با محدودیت نرخ API پیشفرض گیتهاب مواجه میشویم که سقفی برای تعداد فراخوانیهای دریافتی از هر IP خاص در طی یک ساعت تعریف کرده است.
بدین ترتیب خیلی زود با خطای زیر مواجه میشویم:
TypeError: response.map is not a function
به بیان دیگر API آرایه مورد نظر ما را بازگشت نمیدهد. اما خوشبختانه علاوه بر REST API فوق، گیتهاب یک نقطه انتهایی GraphQL نیز عرضه کرده است که در آدرس زیر قابل دسترسی است:
https://api.github/graphql
این بدان معنی است که میتوانیم کد فوق را در زبان با دامنه خاص GraphQL به صورت زیر بازنویسی کنیم:
اگر تاکنون با هیچ کد GraphQL مواجه نشدهاید، کد فوق احتمالاً باعث سردرگمی شما میشود. در ادامه ساختار آن را توضیح میدهیم.
توجه کنید که ما به منظور مقاصد آموزشی از یک پیادهسازی کاملاً سطح پایین و دستی GraphQL استفاده میکنیم. زمانی که در عمل با GraphQL مواجه شوید احتمالاً از طریق Apollo یا Relay خواهد بود. این کتابخانهها به طور خاص صرفاً ابزارهایی هستند که کار با GraphQL را در سمت کلاینت راحتتر میسازند.
بخش چهارم: معادل برنامه Hello World در GraphQL
اغلب ما وقتی با یک زبان برنامهنویسی جدید، هر چند یک زبان با دامنه خاص مانند GraphQL باشد مواجه میشویم، کوهی از اطلاعات تازه را میبینیم که باید یاد بگیریم. برای این که این فرایند، قابلیت مدیریت بیشتری داشته باشد، بهتر است کار خود را همواره با این سؤال آغاز کنیم که معادل Hello World در این زبان چیست. به صورت خلاصه:
یک برنامه Hello, World! عموماً یک برنامه رایانهای است که پیام !Hello, World را در خروجی عرضه کرده یا نمایش میدهد. به دلیل این که نوشتن این برنامه در اغلب زبانهای برنامهنویسی کاملاً آسان است غالباً برای نمایش ساختار مقدماتی آن زبان برنامهنویسی استفاده میشود و به طور کلی نخستین برنامهای است که یادگیرندگان آن زبان مینویسند. برنامه Hello, World! به طور سنتی برای آموزش زبان برنامهنویسی به افراد مبتدی استفاده میشود.
GraphQL به طور کلی به منظور پرسش از فیلدهای خاصی از یک شیء استفاده میشود. این مفهوم به صورت «کوئریهای خاص کلاینت» نیز نامیده میشود. این کوئریها در سطح فیلد دارای تکینگی هستند. در اغلب اپلیکیشنهای کلاینت-سرور که بدون GraphQL نوشته میشوند، سرور تعیین میکند که چه دادههایی در نقاط انتهایی مختلف بازگشت یابد. اما از سوی دیگر یک کوئری GraphQL دقیقاً آن چه را کاربر از آن پرسش کرده است بازگشت میدهد و نه چیز بیشتر.
با این حال آن چه که برنامه Hello World در GraphQL را از معادل آن در هر زبان برنامهنویسی دیگر مانند جاوا اسکریپت جدا میکند، این است که باید کوئری را به چیزی که قابل کوئری زدن است وصل کند.
خوشبختانه چند نقطه انتهایی عمومی GraphQL در اینترنت وجود دارند. نقطه انتهایی دانشگاه استنفورد امکان کوئری زدن به دادههای مقاومت دارویی HIV را در یک نقطه انتهایی (+) که شامل مجموعهای از اطلاعات جمعیتشناختی عمومی مرتب شده بر اساس کشور است فراهم میسازد.
اما ما در این نوشته از نقطه انتهایی دیگری به نام Pokéapi (+) استفاده میکنیم که همه دادههای پوکمان مورد نیاز را ارائه میکند. به کوئری زیر توجه کنید:
کوئری فوق لیستی از 20 پوکمان را از https://pokedex.org/ بازگشت میدهد. اما ما میخواهیم چیزی خاصتر را پیدا کنیم و مقصود اصلی GraphQL نیز جز این نیست.
فرض کنید میخواهیم میانگین وزن تکامل نهایی Pikachu را بدانیم. بدین منظور از کوئری زیر استفاده میکنیم:
لازم به ذکر است که کوئری فوق در واقع اختصاری برای کوئری زیر است:
این واقعیت که GraphQL وقتی شما هیچ اقدامی تعریف نکردهاید، تصور میکند که در حال کوئری زدن هستید، نشان میدهد که کوئریها چه نقش اساسی در GraphQL دارند.
در هر حال از هر کدام از روشهای فوق که استفاده کنیم، پاسخ JSON زیر را دریافت خواهیم کرد:
این بدان معنی است که میتوانیم تابع parse() خود را که در مثال فوق بررسی کردیم، روی این پاسخ JSON اجرا کنیم و نتیجه 29.5 را در DOM عرضه کنیم.
به طور خلاصه میتوان گفت که کوئریها نگاشتهای کوچکی به شکل رشته (String) هستند که از سوی GraphQL برای ناوبری در ساختمان داده جهت یافتن همه آیتمهای درخواستی در یک مسیر منفرد مورد استفاده قرار میگیرند.
این بدان معنی است که میتوانیم مجموعه مشابهی از دستورالعملهای واقعی را به زبان با دامنه خاص GraphQL بنویسیم:
بخش پنجم: مفاهیم GraphQL به چه ترتیبی باید آموخته شوند؟
در این مقاله و تا به اینجا مطالب زیادی در مورد کوئریهای GraphQL آموختیم. با این حال، چند مفهوم دیگر نیز وجود دارند که اگر میخواهید با همه قابلیتهای این زبان آشنا شوید باید بیاموزید:
فیلدها
منظور از فیلد آیتمی در کوئری است که مشابه کلیدها در شیء جاوا اسکریپت عمل میکند. برای نمونه در مثال فوق paper, post_office و travel فیلد هستند.
آرگومانها
اطلاعات اختیاری که میتوان به فیلدها ارسال کرد. برای نمونه در مثال فوق type: drive و amount: 12 آرگومان هستند.
اسامی مستعار
یک نام سفارشی است که باید برای کلید جاوا اسکریپت استفاده شود تا به یک فیلد resolve شود. به صورت پیشفرض کلید شیء همان نام فیلد را دارد. برای نمونه در مثال فوق post_office به {{ ... } :post_office } تبدیل میشود. با این حال میتوان از اسامی مستعار به صورت postOffice: post_office نیز استفاده کرد که مقدار بازگشتی آن {{ ... } :postOffice } است. این حالت نه تنها زمانی که میخواهیم کلید معناشناسی بهتری داشته باشد یا در زمان تعیین نوع مفید است، بلکه هنگامی که از یک فیلد دو بار استفاده میکنیم نیز مفید واقع میشوند. بدین ترتیب کلید پیشفرض post_office دوم از override کردن مقدار post_office اول خودداری میکند.
متغیرها
اگر به تازگی با GraphQL آشنا شدهاید، احتمالاً مانند همه توسعهدهندگان از میانیابی دینامیک در کوئری استفاده میکنید که از طریق template literal انجام مییابد. به بیان دیگر در مورد مثال پوکمان فوق به صورت زیر عمل میکنیم:
pokemon (name: "${dynamicValue)")
اما باید بدانید که GraphQL ابزاری داخلی برای ارسال مقادیر بیرونی به یک کوئری درد. این کار از طریق اعلان یک متغیر در ریشه کوئری صورت میگیرد. برای نمونه در مورد:
query GetPokemonEvolutionWeight($name: string)
و
pokemon (name: $name)
میتوانیم یک شیء از متغیرها را در زمان فراخوانی شدن کوئری به آن ارسال کنیم. توجه کنید که باید یکی از انواع اسکولار مرکزی GraphQL را به متغیر انتساب دهید. برای مثال ما از string استفاده کردهایم. امکان ایجاد انواع سفارشی نیز وجود دارد، اما توضیح این کار خارج از حیطه این مقاله است.
به علاوه شاید علاقهمند باشید کوئریهای قبلی که در این مقاله در GraphQL نوشتیم را با استفاده از قابلیتهای پیشرفتهتر GraphQL بازنویسی کنید.
جهش (Mutation)
تا به اینجا دادهها را از طریق GraphQL صرفاً واکشی کردهایم، اما امکان ارسال دادهها از طریق GraphQL نیز وجود دارد. این کار از طریق هشها انجام مییابد که معادل POST در نقاط انتهای REST API هستند.
ثبت نام (Subscription)
این در واقع کوئری سنتی GraphQL در جهت معکوس است. به جای ارسال یک درخواست به سرور و بازیابی دادهها، به سرور اعلام میکنیم که وقتی دادهها تغییر مییابند به ما اعلام کند و دادههای بهروز شده را چنان که در ثبت نام تعریف شده است به ما ارسال کند.
سخن پایانی
از اینکه این مقاله را در مورد GraphQL مطالعه کردید متشکریم. نکات بسیار زیادی وجود دارند که باید در مورد این زبان بدانید و در این نوشته صرفاً مباحث کاملاً مقدماتی به صورت گذرا مورد بررسی قرار گرفتند. از همین رو برای یادگیری بهتر و بیشتر، بیشک باید انرژی و زمان بیشتری را صرف کنید.
اگر این نوشته برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- مجموعه آموزشهای طراحی سایت
- آموزش مقدماتی GraphQL — از صفر تا صد
- ۱۲ نکته کلیدی برای ارزیابی کتابخانه های جدید جاوا اسکریپت
- راهنمای جامع React (بخش اول) — از صفر تا صد
==