GraphQL چیست ؟ آشنایی مقدماتی با GraphQL…

GraphQL چیست
آشنایی با GraphQL

در ابتدای وب، توسعه APIها کاری بس دشوار بود، تا همین چند سال پیش هم کار سختی بود 😐 روشی که ما API هایمان را دولوپ می کنیم باید با گذشت زمان متحول بشه تا همواره به بهترین روش API بسازیم.

در این چند سال اخیر، GraphQL در جوامع برنامه نویسی و توسعه دهندگان خیلی رایج شده و در حال حاضر کمپانی های بزرگ زیادی به سمت GraphQL رفته اند. GraphQL زبانی کوئری است که توسط فیسبوک در سال ۲۰۱۲ معرفی شد و در سال ۲۰۱۵ به صورت عمومی منتشر شد. وامنشهای زیادی را پس از انتشار به سمت خودش جلب کرد و توسط کمپانی های بزرگی همچون فیسبوک، نت فلکس، گیت هاب ، والمارت، اسپاتیفای و غیره به کار گرفته شد.

در ادامه به شناسایی GraphQLخواهیم پرداخت و می فهمیم که چه چیزی است و چه چیزهایی این زبان کوئری را دارای درک و استفاده آسان کرده است.

GraphQL چیست ؟ مبانی و اصول اولیه GraphQL چیست؟

به طور خلاصه، GraphQL یک سینتکس (syntax – نحو) است که نحوه درخواست داده را توصیف می کند و به طور کلی برای بارگذاری داده از سمت سرور برا کلاینت استفاده می شود. GraphQL دارای سه ویژگی اصلی است:

  1. به کلاینت اجازه می دهد تا مشخص کند دقیقا چه داده ای را نیاز دارد.
  2. جمع آوری داده ها از چند منبع را آسان می کند.
  3. برای توصیف داده ها از یک نوع سیستمی (type system) استفاده می کند.

حال بیاید با بررسی مشکلات REST پیش بریم و اینکه GraphQL چطور آنها را حل می کنه. همچنین خواهیم فهمید که چرا کمپانی ها به سمت GraphQL سوییچ کرده اند و گرف-کیو ال آینده ی APIها خواهد بود.

REST

مدت ها پیش که طراحی API را از SOAP به REST تغییر دادیم، فکر می کردیم که این تغییر به توسعه دهندگان در کارشان انعطاف پذیری بیشتری خواهد داد. منکر این نمیشیم که در زمان هایی به درستی از پس این مسئله بر اومد و در اون زمان شدیدا نیاز بهش احساس میشد. همانطور که برنامه ها (اپلیکیشن ها) و وب روز به روز به طور طبیعی پیچیده تر می شوند و تکامل پیدا می کنند، با این حال RESTهمچنان مشکلاتی دارد. باید به این مشکلات بپردازیم:

نقاط نهایی بسیار (A lot of endpoints)

هر منبع (resource) در REST با یک نقطه انتهایی نمایش داده می شود. در نتیجه در اپلیکیشن دنیای واقعی برای منابع بسیاری که داریم، نقاط نهایی زیادی خواهیم داشت. اگر می خواهید که یک درخواست (Request) گرفتن (GET) را انجام دهید، به یک نقطه نهایی مختص به آن و با پارامترهای خاص آن نیاز دارید و از طرفی اگر میخواهید requestاز نوع POST انجام دهید، به یک نقطه نهایی دیگری برای آن request نیاز دارید.

اما مشکل کجاست؟ بیاید در نظر بگیرید ما در حال ساخت یه شبکه مجازی بزرگ همچون فیسبوک هستیم. ما به تعدا زیادی endpoint نیاز داریم که به منزله تعداد بیشتری توسعه دهنده و زمان بیشتر برای توسعه این APIها می باشد.

واکشی خیلی کم و خیلی زیاد (Over-fetching and under-fetching of information)

یکی از بزرگترین مشکلاتی که خیلی از توسعه دهندگان را رنج می دهد همین Over-fetching و under-fetching اطلاعات از طریق REST API می باشد. این مشکل به این دلیل است که REST APIهمخواره یه ساختار ثابت را بر می گرداند. ما نمی توانیم دقیقا داده ای که به آن نیاز داریم را بدون اینکه یه endpoint مختص برایش بسازیم را get کنیم.

در نتیجه اگر ما فقط یه قسمت کوچکی از داده ها را می خواهیم باید کل object را بگیریم. برای مثال اگر ما فقط نیاز داریم که نام و نام خانوادگی و سن را در یک REST API بگیریم، روشی وجود نداره که دقیقا این اطلالاعات را بگیریم بدون اینکه کل object را واکشی کنیم.

همچنین مشکلی با under fetchingنیز وجود داره،. اگر ما نیاز داشته باشیم که اطلاعات را از دو تا resource متفاوت بگیریم، نیاز داریم که دو تا نقطه نهایی متفاوت را فراخوانی کنیم. در اپلیکیشن های بزرگ، این مسئله به خوبی متناسب نمی شود چرا که مواردی پیش می آید که ما فقط نیاز داریم قسمتی از یه داده را داشته باشیم و نه کل object را. حال فرض کنید ما اپلیکیشنی را ساخته ایم که ۱۰۰ تا endpointدارد. میزان کار و کدی که نیاز است تا نوشته شود را تصور کنید. این مسئله با توجه به زمان سخت تر نیز می شود. نگهداری از کد نیز سخت میشه و توسعه دهنده ها رد طول پردازش گیج می شوند 🙁

نسخه کرن (Versioning)

یکی از نقاط دردناک REST به نظر من نسخه کردن آن می باشد. با REST API خیلی رایج هست که APIهایی را با v1 یا v2 ببینید. در GraphQL نیاز به نسخه بندی نمی باشد چرا که شما می توانید APIخود را تکامل بدید و موارد چدید به آن اضافه یا موارد قدیمی را حذف کنید.

در GraphQL همه ی چیزی که شما نیاز دارید تا APIخود را تکامل یدید، نشوتن کد جدید هست. شما می توانید کوئری ها، تغییرات جدید یا typeهای جدید را بدون نیاز به جهش به APIجدیدی انجام دهید.

در نتیجه شما GraphQL API یا نقاط پایانی همچون زیر نخواهید دید:

https://example.com/api/v1/users/12312
https://example.com/api/v2/users/12312

چرا آینده در دستان GraphQL است؟

برگردید به سال ۲۰۱۲، فیسبوک در حالی که در حال توسعه موبایل اپلیکیشن خود بود، با مشکلاتی روبرو شد که آنها را با ایجاد GracphQL هدایت کرد. این مشکلات بسیار رایج است، به خصوص زمانی که در مورد طراحی RESTful API صحبت می کنیم. همانطور که مطرح شد این مشکلات عبارت هستند از:

  • عملکرد ضعیف
  • نقاط پایانی زیاد
  • واکیشی بیش از حد یا کم داده ها
  • پرش به نسخه ی جدید هر گاه نیاز به تغییری داشته باشیم (اضافه یا حذف)
  • درک سخت مفاهیم

توسعه دهندگان فیسبوک با داشتن مفاهیم بسیار در ذهنشون، روش بهتری را برای طراحی APIها توسعه دادند و در انتها نام آن را GraphQL نهادند. به طور پایه، جایگزینی برای RESTfulمی باشد با کلی بهبود ها.

مشکل !

همون طور که بالاتر گفتم، GraphQL از زمان فیسبوک قدیمی شروع شد ولی هر برنامه نیوس دیگه ای نیز با مشکلات و محدودیت های REST سنتی و سابق دست و پنجه نرم کرده.

برای مثال تصور کنید که میخواهیم لیستی از پست ها را نمایش دهیم و زیر هر پست لیستی از لایک های آن را، شامل userName و Avatar. به اندازه کافی راحت هست و شما آرایه API پست های خود را تغییر می دهید و کاری می کنید شامل Like ها نیز شود.

خب حالا وقتش رسیده که نیاز بالا داخل اپلیکیشن موبایل نیز حل بشه و آنجا نیز پست ها با لایک هایشان نمایش داده شوند، ولی متاسفانه اون داده های اضافی که شامل لیست like ها زیر پست ها بود منجر به کُند شدن می شود. پس الان شما نیاز به دو تا endpoint (نقطه پایانی – نهایی) دارید، یکی برای like ها و یکی برای بدون آنها.

حال بیاید یه فاکتور دیگه نیز اضافه کنیم، زمانی رو در نظر بگیرید که post ها داخل mysqlذخیره می شوند و like ها داخل redis ذخیره می شوند! حال چه کار می کنید؟

حال این سناریو را با جند منبع داده و API clientهایی که فیسبوک باید آنها را مدیریت میکرد، قیاس کنید! اینجاست که کاملا متوجه می شید محدودیت های REST API کجاها بوده 🙂

راهکار 🙂

راهکاری که فیسبوک برای مسئله فوق مطرح کرد، خیلی ساده بود، به جای آنکه چندین endpoint به صورت «گُنگ» داشته باشیم، یک نقطه پایانی «هوشمند» داشته باشیم که بتواند کوئری های پیچیده داشته باشد و سپس خروجی داده را به هر شکلی که کلاینت نیاز دارد شکل دهی کند.

به طور عملی، لایه GraphQL بین کلاینت و یک یا چند منبع داده زندگی می کند و درخواست های کلاینت را دریافت می کند و داده های لازم را مطابق با دستور العمل شما واکشی (fetch) می کند. سردرگم شدید (گیج شدید) ؟ وقت تشبیه رسید.

مدل قدیمی REST همچون سفارش پیتزا می باشد، سپس مواد غذایی را می گیرد، سپس با خشکشویی تماس می گیرد تا لباس های شما را بگیرد. سه مغازه و سه تماس تلفنی.

نقاط نهایی در مدل قدیمی REST
نقاط نهایی در مدل قدیمی REST

در طرف دیگر GraphQL شبیه به این هست که یک دستیار شخصی دارد: زمانی که شما به او آدرس سه مکان را می دهید، به راحتی می توانید هر آنچه می خواهید را از او درخواست کنید («لباس هام رو از خشکشویی بگیر، یه پیتزا کوچیک بخر، یه شانه تخم مرغ دوزرده بخر») تا او تهیه کنه و به دست شما برسونه.

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

نقاط انتهایی تک (Single endpoint)

نیازی به ساخت endpoint های زیاد نیست! با GraphQL ما تنها یک endpoint را می گیریم و با آن، ما می توانیم به هر اندازه که داده نیاز داریم با یک request بگیریم. اساسا GraphQL تمام کوئری های شما، دگرگونی ها و وجه اشتراکات را در یک نقطه نهایی، پنهان (قُنداق کردن) می کند و آن را در دسترس شما قرار می دهد. این سیکل توسعه شما را بهبود می دهد چرا که نیازی نیست که دو تا request برای گرفتن داده از دو resource متفاوت بنویسید. همچنین تصور کنید که ما یک اپلیکیشن بزرگی را می سازیم، ما تعداد زیادی نقطه نهایی و همچنین تعداد زیادی خط کد همچون زمان REST نخواهیم داشت. ما یک نقطه نهایی خواهیم داشت و با آن endpoint، می توانیم تا دلتون بخواد request بزنیم 🙂

Single endpoint

همچنین، همان طور که در بالا گفته شد با روپیکرد «فقط یک نقطه پایانی»، API شما را خود مستند می کند و نیازی نیست مستند سازی کنید تا توسعه دهندگان تان بدانند باید چه کنند، چرا که توسعه دهندگانتان خودشون می دونند که باید چه کنند و چطور از آن استفاده کنند. آنها APIرا تنها با مشاهده کد آن خواهند فهمید. در مورد این مسئله بیشتر صحبت خواهیم کرد. جادویی به نظر میاد ولی این فقط GraphQL است :))

با GraphQL شما فقط داده ای که به آن نیاز دارید را Fetch می کنید

دیگه واکشی کم یا بیش از حد داده ها نخواهیم داشت. شما فقط داده ای که به آن نیاز دارید را واکشی می کنید. عملکرد ضعیفی که از RESTful بالاتر ازش نام بردیم به همینجا بر میگرده. پس دیگه مشکلی با این عملکرد ضعیف نداریم، چرا که GraphQLاین مسئله را حل کرده و عملکرد را بهبود می دهد مخصوصا در مواردی که اتصالات نتورک ضعیف هستند.

GraphQL شروع به ساخت و سازگار بودن API را آسان می کند

خیلی از افراد فکر می کنند GraphQL ویچیده است چرا که شامل فقط یک Schema است و تنها از یک endpointاستفاده می کند. زمانی که شما شروع به نوشتن API با آن می کنید، به این دست می یابید که بسیار ساده تر از آن چیزی است که در موردش فکر می کردید. API که «فقط یک نقطزه نهایی» داشته باشد به هنگام توسعه اپلیکیشن وب یا موبایل، به شما بسیار کمک خواهد کرد. این باعث می شود که API خود مستندتر شود و نیازی نیست که از طرف شما مستندات زیادی در مورد آن نوشته شود.

اگر از جاوا اسکریپت به عنوان زبان اصلی خود استفاده نمی کنید، هیچ مسئله ای نیست. GraphQL زبانی کوئری محور agnostic می باشد که به این معنی است می توانید آن را با هر زبانی استفاده کنید. در حال حاضر که این مقاله نوشته می شه، GraphQL بیش از ۱۲ زبان را پشتیبانی می کند.

GraphQL آینده است

این حقیقت که GraphQL زبانی متن باز است، خود گواه بر این دارد که مشارکت کنندگان آن زیاد است و در حال بهبود دادن آن. زمانی که فیسبوک آن را به جوامع ارائه کرد، توسعه دهندگان بسیاری جذب آن شدند و بهبود های فراوانی به آن دادند. بعضی در حال حاضر می پرستند که واقعا GraphQLدر حال جایگزینی با RESTful است و روش جدید ساخت API خواهد شد؟

اول از همه به نظر من، GraphQL تنها روشی جدید برای ساخت APIمی باشد. و زمانی که به یادگیری آن پرداختم، یه این نتیجه رسیدم که GraphQL ویژگی های ضروری زیادی را دارد که برای اپلیکیشن ها مدرن نیازی مبرم می باشد.

به نظر من GraphQL آینده APIها خواهد بود. به همین دلیل هست که کمپانی های بزرگ متمرکز بر آن شده اند.

اجزای GraphQL :

در عمل یک GraphQL API در سه بلوک اصلی سازمان دهی می شود: طرح واره (Schema)، کوئری ها (Query) و رافع ها (resolver).

کوئری ها در GraphQL :

درخواستی که به دستیار شخصی GraphQL خود می دهید، کوئری می باشد و چیزی شبیه به این می باشد:

query {
  stuff
}

ما کوئری جدید را با کلمه کلیدی query تعریف می کنیم و سپس فیلدی به نام staff را درخواست می کنیم. نکته جالب در مورد کوئری های GraphQL این است که آنها از فیلدهای تودرتو پشتیبانی می کنند پس ما میتوانیم به یک سطح عمیقتر برویم:

query {
  stuff {
    eggs
    shirt
    pizza
  }
}

همانطور که مشاهده می کنید، کلاینتی که کوئری را می سازد، نیازی به این ندارد که داده ها از کدام فروشگاه تهیه می شوند.فقط آن چیزی که میخواهید را درخواست دهید و اجازه دهیه GraphQL Server باقی موارد را مراقبت کند.

شایان ذکر است که فیلدهای کوئری همچنین می توانند به آرایه ها (arrays) اشاره کنند. برای مثال، در ادامه الگوی رایجی برای زمانی که لیستی از پست ها کوئری می شود آورده شده است:

query {
  posts { # this is an array
    title
    body
    author { # we can go deeper!
      name
      avatarUrl
      profileUrl
    }
  }
}

فیلدهای کوئری همچنین از آرگومان ها (arguments) پیشتیبانی می کنند. اکر می خواهم که پست خاصی را نمایش دهم، می توانم یک آرگومان id به فیلد post اضافه کنم.

query {
  post(id: "123foo"){
    title
    body
    author{
      name
      avatarUrl
      profileUrl
    }
  }
}

در انتها اگر بخواهم اون آرگومان id را dynamic کنم، می توانم یک متغیر (variable) تعریف کنم و سپس از آن داخل کوئری استفاده کنم (توجه به این داشته باشید که در اینجا ما کوئری را نام گذاری (naming) نیز می کنیم ):

query getMyPost($id: String) {
  post(id: $id){
    title
    body
    author{
      name
      avatarUrl
      profileUrl
    }
  }
}

روشی مناسب برای تمرین تمامی این موارد استفاده از GitHub’s GraphQL API Explorer می باشد. برای مثال کوئری زیر را بهش دهید و تست کنید:

query {
  repository(owner: "graphql", name: "graphql-js"){
    name
    description
  }
}

توجه داشته باشید همین که شما شروع به تایپ کردن Field Name زیر description کنید، IDE خود به صورت خودکار Filed anme ها ممکن را به صورت مستقیم از خود GraphQL API به صورت auto-completed پیشنهاد می دهد. شسته و رفته !

جهت مطالعه بیشتر در زمینه کویئری های GraphQL پیشنهاد می کنم نگاهی به اینجا بیاندازید. (مقاله Anatomy of a GraphQL Query)

رافع ها (راه حل ها) Resolvers :

حتی بهترین دستیاران هوشمند تو دنیا هم بدون این که از شما آدرسی بگیرند نمیتونند برند و لباس های آماده ی شما را از خشکشویی تحویل بگیرند.

به طور مشابه GraphQL server شما نیز نمیتونه بفهمه که با کوئری فرستاده شده از سمت شما چه کنه، مگر اینکه با استفاده از resolver ها بهش بگید.

یک resolver به GraphQL می گه که چطور و کجا داده را منطبق با فیلد داده شده واکشی کند. برای مثال، اینجا چیزیست که resolver برای فیلد post در بالا دیده می شود (از ابزارApollo’s GraphQL-Tools schema generator استفاده شده است )

Query: {
  post(root, args) {
    return Posts.find({ id: args.id });
  }
}

ما resolver را روی Query قرار می دهیم چرا که ما میخواهیم برای post مستقیما در سطح root کوئری داشته باشیم. اما شما می توانید برای sub-fieldها نیز resolverداشته باشید، مانند فیلد نویسنده ی پست (post‘s author) :

Query: {
  post(root, args) {
    return Posts.find({ id: args.id });
  }
},
Post: {
  author(post) {
    return Users.find({ id: post.authorId})
  }
}

و توجه داشته باشید که resolverهای شما محدود به برگرداندن اسناد پایگاه داده های شما نیستند. برای مثال ممکن است شما بخواهید یک commentsCount به نوع Postخود اضافه کنید:

Post: {
  author(post) {
    return Users.find({ id: post.authorId})
  },
  commentsCount(post) {
    return Comments.find({ postId: post.id}).count() 
  }
}

مفهوم کلیدی جهت درک در اینجا این هست که با استفاده از GrasphQL ، شِمای (schema-طرحواره) API شما و طرحواره پایگاه داده شما از هم جدا هستند. به عبارت دیگر، ممکن است در پایگاه داده های شما هیچ یک از فیلد های author یا commentsCount وجود نداشته باشد، اما ما می توانیم آنها را با قدرت resolverها شبیه سازی کنیم.

همانطور که مشاهده کردید شما می توانید هر کدی که می خواهید را داخل resolverها بنویسید. به همین دلیل است که می توانید آنها را وادار به تغییر محتویات پایگاه داده خود کنید، در این صورت آنها به عنوان رافع های دگرگونی (mutation resolvers) شناخته می شوند.

طرح واره – Schema :

همه این موارد خوب توسط «سیستم انواع طرح واره GraphQL » ممکن می شوند . هدف در این مقاله این هست که یک مرور کلی و بیش از یک مقدمه کامل داشته باشیم و درد نتیجه خیلی وارد جزییات نمی شویم.

همانطور که قبلا هم گفته شد، تشویق می کنم اگر دنبال یادگیری بیشتر در مورد GraphQWLهستید، یه سری به مستندات آن در اینجا بزنید.

در ادامه یه چند تا سوال رایج در مورد GraphQl پاسخ داده شده است:

منابع و مراجع:

رابطه ی بین GraphQL و پایگاه داده های Graph چیست؟

هیچ رابطه ای نیست، GraphQLهیچ چیزی برای انجام دادن با پایگاه داده های گراف همچون Neo4j ندارد. قسمت «گراف – graph» از ایده خزیدن در گراف APIشما با استفاده از فیلد ها و sub-field ها آومده است. در حالی که QL مخفف «زبان کوئری – query language» می باشد.

من به طور کامل با RESTراحت هستم، چرا باید به GraphQL سوییچ کنم؟

اگر هنوز به نقاط درد REST که GraphQLقرار است آنها را تسکین دهد بر نخورده اید، چیز خیلی خوبی است.

استفاده از GraphQL ذروی REST در این مواقع احتمالا تاثیر زیادی در تجربه کاربری نخواهد داشت و در نتیجه سوییج به آن مسئله مرگ و زندگی نخواهد بود. همانطور که گفته شد، من قطعا توصیه می کنم اگر فرصتی پیدا کردید GraphQLرا روی یک پروژه کوچک اجرا کنید.

آیا می توانم GraphQL را بدون React/Relayیا هر کتابخانه ی دیگری استفاده کنم؟

بله می توانید، از آنجا که GraphQL تنها یک مشخصات است، شما می توانید از آن با هر کتابخانه یا سیستم عاملی یا با هر کلاینتی استفاده کنید( برای مثال Apollo کلاینت های GraphQLرا برای وب، ios، انگولار و … دارد ) یا فراخوانی های خود به GraphQLرا بسازید.

با توجه به عبارت «اجازده دهید کلاینت داده ای که نیاز دارد را بخواهد» خیلی احساس امنیت نمی کنیم و …

با توجه به اینکه شما resolverهای خود را می نویسید، این سمت شماست که هر نوع دغدغه امنیتی در هر سطحی را آدرس دهی کنید.

برای مثال اگر شما به کلاینت اجازه دهید تا پارامتر limit مشخص کند تا تعداد اسنادی که دریافت می کند را کنترل کند، شما احتمالا می خواهید تا آن عدد و تعداد را محدود کنید تا از حملات denial-of-service-style جلوگیری کنید که در آن کلاینت ها میلیون ها و میلیون ها درخواست و بارها و بارها می دهند.

برای شروع به چه چیزهایی نیاز دارم؟

برای شروع حداقل به دو componentنیاز دارید تا GraphQLرا اجرا کنید:

  • یک GraphQl Serverتا API های شما را ارائه دهد
  • یک GraphQL client تا به نقاط نهایی شما متصل شود.

اکوسیستم

به زودی تکمیل خواهد شد 🙂