چگونه با استفاده از GraphQL، وبسایت خود را تمیز و قابل نگهداری کنیم؟

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 20 خرداد 1398
دسته بندی ها : جاوا اسکریپت

سرویس‌های REST API، دیتابیس‌های SQL، فایل‌های markdown، فایل‌های متنی، سرویس‌های SOAP... آیا می‌توانید راه دیگری برای ذخیره و رد و بدل داده و محتویات بیان کنید؟ وبسایت‌های تولیدی معمولا با چند نوع سرویس‌ها و راه‌های ذخیره‌سازی داده‌ها کار می‌کنند؛ پس چگونه می‌توان این پیاده‌سازی را به صورت تمیز و قابل نگهداری حفظ کرد؟

هر وبسایت Node.js، بدون توجه به این که یک برنامه تک صفحه‌ای بوده، یا این که یک وبسایت معمولی می‌باشد، باید به یک سیستم یا سرویس جداگانه متصل شود. حداقل این وبسایت باید محتویات را از فایل‌های markdown یا یک cms بدون head دریافت کنند. اما نیاز برای سرویس‌های دیگر سریعا ظاهر می‌شود. در ابتدا، نیاز به یک فرم ارتباطی؛ شما باید ارسالیه‌های آن را ذخیره کنید. سپس یک نیاز به یک جستجوی متنی کامل؛ شما باید سرویسی پیدا کنید که شما را قادر بسازد تا فهرست‌هایی را ساخته، و در آن‌ها جستجو کنید. و این لیست همینطور بسته به حجم پروژه شما ادامه دارد.

مشکل این مسئله چیست؟ خب، در ابتدا هیچ مشکلی وجود ندارد. وقتی که شما انگیزه دارید تا یک پروژه را به اتمام برسانید، شما یک کامپوننت برای هر کدام از این عملکردها می‌سازید. ارتباطات داخل کامپوننت‌های مربوطه کپسوله‌سازی شده‌اند، و پس از چند تست سریع، شما خواهید دید که همه چیز کار می‌کند. مشتری خوشحال است که پروژه‌اش قبل از مهلت نهایی تحویل داده شده است، و به عنوان یک اثر جانبی، شما همچنین تبدیل به یک متخصص در زمینه‌های «محتویات به عنوان یک سرویس» (CaaS = Content as a Service)، سرویس‌های ارسال فرم، و بازسازی لیست جستجوی خودکار شده‌اید.

شما وبسایت را راه‌اندازی کردید و ترفیع گرفتید! همچنین دانش و جزئیات پروژه هم در دستان شما هستند.

در چند هفته، از همکاران شما درخواست می‌شود که همین پروژه را انجام دهند. مشتری می‌خواهد از یک فراهم کننده خدمات جستجو متفاوت استفاده کند؛ زیرا سرویس‌های اصلی خیلی گران هستند. توسعه دهندگان همچنین در حال کار بر روی یک پروژه دیگر هستند که به یک فرم ارتباطی نیاز دارد، پس آن‌ها هم درباره استفاده از کامپوننت مشابه فکر کردند، اما ارسالات را در یک سرویس دیگر ذخیره می‌کنند. پس آن‌ها به سراغ شما می‌آیند، تا درباره مشخصات پیاده‌سازی شما سوال کنند.

وقتی که شما در نهایت از جستجو در خاطرات خود تسلیم می‌شوید، آن‌ها باید تحقیقاتی مشابه به تحقیقات شما را انجام دهند، تا پیاده‌سازی مورد نظر را در یابند. رابط کاربری به قدری با عملکرد جفت شده است که وقتی آن‌ها می‌خواهند از کامپوننت‌ها مجددا استفاده کنند، احتمالا باید از ابتدا همه چیز را پیاده‌سازی کنند. (و شاید برخی قطعه کدهای قدیمی را کپی و پیست کنند)

سطح مناسب چکیده‌سازی

پس چگونه می‌توانیم از این مشکلات جلوگیری کنیم و کد خود را به صورت قابل نگهداری و تمیز حفظ کنیم؟ نگاهی به شکل بالا که من ارتباطات با خدمات ثالث و رابط را تقسیم کردم داشته باشید. مشخصات هر API سرویس، در میان‌افزار موجود بر روی backend وبسایت پیاده‌سازی شده است. کامپوننت‌های موجود در frontend همگی از یک راه برای دریافت و ارسال داده‌ها استفاده می‌کنند: GraphQL.

GraphQL

پس GraphQL چیست و چرا از آن برای ارتباط بین frontend و backend استفاده کنیم؟ GraphQL یک زبان کوئری، یا یک پروتکل است که دقیقا برای همین هدف تاسیس شد: جدا کردن داده‌هایی که frontend وبسایت نیاز دارد، از کوئری‌های مورد نیاز برای دریافت آن‌ها. GraphQL از نظر عملکرد مشابه یک REST API است؛ زیرا شما را قادر می‌سازد تا برای داده‌ها کوئری کنید.

تفاوت اصلی، در روش درخواست برای اطلاعات است. فرض کنید یک توسعه دهنده جدید بر روی پروژه، وظیفه ساخت یک صفحه بلاگ را دارد. صفحه مورد نظر باید پست‌های بلاگ که داخل یک CMS بدون head ذخیره شده‌اند را نمایش دهد. من از Kentico Cloud، یک پلتفرم «محتویات به عنوان یک سرویس» (CaaS = Content as a Service) استفاده می‌کنم که شما را قادر می‌سازد تا انواع مختلف محتویات را در یک ساختار سلسله مراتبی تمیز ذخیره کنید و آن‌ها را از طریق یک REST API دریافت کنید. از این رو درخواست GET برای داده‌ها، با استفاده از یک REST API می‌تواند چنین ظاهری داشته باشد:

https://deliver.kenticocloud.com/{projectID}/items?system.type=blog_post

یک پاسخ نمونه به این صورت خواهد بود:

{
  "items":[ 
    { 
      "system":{ 
        "id":"0282e86e-8c72–47f3–9d3d-2acf93a8986b",
        ...
        "last_modified":"2018–09–18T10:38:19.8406343Z"
      },
      "elements":{ 
        "title":{ 
          "type":"text",
          "name":"Title",
          "value":"Hello from new Developer Evangelist"
        },
        "content":{
          ...
        }
        ...
      }
    }
  ]
}

پاسخ مورد نظر، شامل داده‌های تمام پست‌های بلاگ در قالب JSON می‌باشد. با توجه به این که صفحه مورد نظر فقط لیستی از پست‌های بلاگ را نمایش می‌دهد، مقدار زیادی از داده‌های برگشتی (از جمله فیلد content) زائد هستند؛ زیرا ما نمی‌خواهیم آن‌ها را نمایش دهیم. برای ذخیره پهنای باند (که معمولا برای شما هزینه دارد)، توسعه دهنده از ویژگی افزوده columns استفاده می‌کند:

https://deliver.kenticocloud.com/{projectID}/items?system.type=blog_post&elements=title,image,teaser

آن‌ها باید مشخصات API را بدانند و احتمالا وقتی که در حال ساخت کوئری هستند، مرجع آن را در یک صفحه مرورگر دیگر به صورت باز شده داشته باشند.

دریافت داده‌های مشابه با GraphQL بسیار آسان‌تر است. طرح GraphQL به طور بومی چیزی که frontend قادر به رندر کردن آن است را توصیف می‌کند. توسعه دهنده باید در یک نشانه‌گذاری مشخص کند که چه داده‌هایی دریافت می‌شوند:

query BlogPosts {
  getBlogPosts {
    elements {
      title
      image
      teaser
    }
  }
}

حال وقتی که شما تصمیم می‌گیرید تا ذخیره‌سازی داده را از CMS بدون head به فایل‌های markdown یا دیتابیس SQL تغییر دهید، پیاده‌سازی صفحه بلاگ تغییری نخواهد کرد. کوئری GraphQL همچنان به صورت مشابه خواهد بود.

چگونه ممکن است؟ بیایید چند لحظه به اعماق مسئله نگاه داشته باشیم. جداسازی پیاده‌سازی frontend از سرویس‌های خارجی، با استفاده از این بخش‌ها تحقق می‌یابد:

  • طرح GraphQL
  • Resolverهای GraphQL
  • سرور Apollo

طرح GraphQL

طرح GraphQL بسیار شبیه به نمودارهای کلاس می‌باشد. این طرح مدل‌های داده، مانند BlogPost یا FormSubmissionn، و کوئری‌های GraphQL را مشخص می‌کند.

در بالا می‌توانید مثالی از یک طرح مدل‌های داده مربوط به یک وبسایت ساده را مشاهده کنید. دقت کنید که typeهای تعریف نشده‌ای مانند SystemInfo یا AssetElement هم وجود دارند. من آن‌ها را از این تصویر حذف کردم؛ زیرا آن‌ها بعدا به طور خودکار تولد مولد type مربوط به CMS بدون سر ایجاد خواهند شد.

سپس کوئری‌ها و جهش‌ها (فراخوانی‌هایی که داده‌ها را تغییر داده و ذخیره می‌کنند)، نحوه دریافت و دستکاری داده‌ها در این مدل، مانند دریافت داده‌ها برای BlogPost یا ارسال یک FormSubmission را توصیف می‌کنند. این مسئله به مانند یک نمودار کلاس برای لایه میان‌افزار وبسایت است.

Resolverها

Resolverها، پیاده‌سازی واقعی کوئری‌های تعریف شده در بالا، مانند MySQL resolver، Kentico Cloud resolver و... هستند. آن‌ها به کوئری‌های مشخصی از طرح اختصاص داده شده‌اند و مسئول پردازش آن‌ها هستند. پس وقتی که یک کامپوننت frontend می‌خواهد پست‌های بلاگ را با استفاده از کوئری getBlogPost در GraphQL به دست بیاورد، سرور resolver ثبت شده برای آن کوئری (Kentico Cloud resolver) را انتخاب کرده، و فراخوانی می‌کند. Resolver مورد نظر از REST API مربوط به CMS بدون سر برای دریافت محتویات در قالب JSON استفاده کرده، و آن را به عنوان یک آرایه آبجکت برای کامپوننت فراهم می‌کند.

در این موقعیت ساده، resolverها به کوئری‌ها و جهش‌ها متصل نیستند؛ اما یک resolver می‌تواند در هر چند مورد از آن‌ها که بتواند از پسشان بر بیاید، ثبت شود. MySQL resolver در حال حاضر کاری برای انجام دادن ندارد، اما بعدها وقتی که عملکرد وبسایت رشد می‌کند و ما تصمیم می‌گیریم که برخی ورودی‌های حساس کاربر را به صورت محلی و با استفاده از یک دیتابیس ذخیره کنیم، کاربری خواهد بود.

Apollo همه چیز را به هم متصل می‌کند

آخرین قطعه پازل ما، سرور Apollo می‌باشد. Apollo چسبی است که تمام این بخش‌ها را به هم متصل می‌کند. Apollo یک کتابخانه و یک فریم‌وورک است که طرح GraphQL را به سرور HTTP در Node.js متصل می‌کند. من به شخصه از Express به عنوان یک سرور HTTP استفاده می‌کنم، اما شما شاید از Connect، Restify یا Lambda هم خوشتان بیاید.

Apollo دو بخش دارد: سرور و کلاینت. سرور به عنوان میزبانی برای طرح GraphQL کار کرده، و درخواست‌های GraphQL را مدیریت می‌کند. پس هر زمان که frontend یک کوئری GraphQL را فراخوانی کند، سرور Apollo به resolver مناسب نگاه می‌کند، منتظر می‌ماند تا داده‌ها را پردازش کند و پاسخ خود را منتقل کند. سرور Apollo اغلب به عنوان یک تبدیل کننده ساده از هر رابط سرویسی به GraphQL، و وقتی شما باید با یک سیستم ادغام شوید که به طور بومی GraphQL را پشتیبانی نمی‌کند، استفاده می‌شود.

کلاینت Apollo یک ماژول است که به frontend یک وبسایت متصل می‌شود و اجرای کوئری‌های GraphQL را ممکن می‌سازد.

Boilerpate برای تسریع روندها

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

منبع

مقالات پیشنهادی

چگونه برنامه‌های JavaScript خود را با استفاده از حلقه‌ها بهینه‌سازی کنیم؟

یکی از آسان‌ترین و مورد غفلت‌ترین کارهایی که می‌توانید برای تسریع کارایی برنامه JavaScript خود انجام دهید، یادگیری نحوه صحیح نوشتن بیانیه‌های حلقه‌ای...

کلید طراحی یک وبسایت عالی ، تعامل با مشتریست

كد نويسي سايت هاي بزرگ ميتونه بسيار زمان گير باشه به همين خاطر نياز به يك رابطه موفق و مناسبي بين طراح و مشتري هست . نگاهي به نمونه كار هاي خود بياندا...

چگونه کد خود را با استفاده از عملگر گسترش، ساده‌سازی کنیم؟

اکثر مردم می‌دانند که Object، Array، Set و موارد مشابه قابل تکرار هستند. این که تمام انواع اولیه هم operandهای معتبر عملگر گسترش هستند، ما را قادر می‌...

چگونه بازی خود را در میان چند دستگاه همگام‌سازی کنیم؟

بازی‌های معمولی در پایین‌ترین سطح خود می‌توانند به قدم‌های کوچکی تقسیم شوند که توسط بازیکن برداشته می‌شوند. این قدم‌ها، نوبت نام دارند و در هر نوبت یک...