كيف تبدأ استخدام GraphQL مع Node.js لبناء واجهة برمجة تطبيقات حديثة

دقائق القراءة: 7

مقدمة إلى GraphQL مع Node.js

إذا كنت تبحث عن طريقة حديثة ومرنة لبناء واجهات برمجة التطبيقات، فإن GraphQL يُعد خياراً ممتازاً، خصوصاً عند استخدامه مع Node.js. الفكرة الأساسية من هذا المشروع التعليمي هي توضيح كيف يوفّر الخادم البيانات عبر GraphQL، وكيف يستطيع العميل طلب ما يحتاجه فقط بدقة وبدون استدعاءات زائدة.

بعكس أسلوب REST التقليدي الذي يعتمد غالباً على عدة endpoints لكل نوع من البيانات، يوفّر GraphQL نقطة وصول واحدة فقط، بينما يحدد العميل بنفسه شكل البيانات التي يريد استرجاعها. هذه المرونة تقلل الهدر في نقل البيانات وتمنح المطور تحكماً أكبر في الاستعلامات.

البدء باستخدام GraphQL مع Node.js لبناء تطبيقات وخوادم حديثة

ماذا ستتعلم في هذا الدليل؟

  • إعداد مشروع بسيط يتكون من عميل وخادم.
  • تعريف مخطط Schema في GraphQL.
  • إضافة دوال Resolver لمعالجة الطلبات.
  • تشغيل خادم باستخدام Apollo Server.
  • إنشاء عميل بسيط يجلب البيانات من الخادم.
  • عرض البيانات داخل الصفحة بشكل مباشر.

البدء في إعداد المشروع

تثبيت Node.js وإنشاء هيكل المجلدات

قبل أي شيء، تأكد من تثبيت Node.js على جهازك. بعد ذلك، أنشئ مشروعاً يحتوي على مجلدين: أحدهما للعميل والآخر للخادم.

📁 project
├── 📁 client
└── 📁 server

بعد إنشاء البنية الأساسية، انتقل إلى مجلد server وشغّل أمر تهيئة المشروع:

npm init

وإذا كنت تريد إنشاء الملف بسرعة دون المرور بالأسئلة التفاعلية، استخدم:

npm init -y

هذا الأمر ينشئ ملف package.json الذي سيحتوي على إعدادات المشروع واعتماداته.

تثبيت الحزم الأساسية

نحتاج أولاً إلى تثبيت مكتبة graphql، وهي المسؤولة عن بناء المخطط ومعالجة الاستعلامات:

npm install graphql

بعدها نثبّت apollo-server لأنه يوفّر طبقة جاهزة لتشغيل خادم GraphQL بسهولة:

npm install apollo-server

من المهم أن تعرف أن Apollo ليس مجرد أداة واحدة، بل منظومة متكاملة للعمل مع GraphQL. وأكثر جزأين شيوعاً فيها هما:

  • Apollo Client: للتعامل مع الواجهة الأمامية وجلب البيانات من GraphQL API.
  • Apollo Server: لإنشاء الخادم ومعالجة الاستعلامات والإجابات.

كيف تعرّف GraphQL Schema؟

يمثل Schema العمود الفقري لأي تطبيق GraphQL. فهو يحدد شكل البيانات المتاحة، وأنواعها، والحقول الموجودة داخل كل نوع، إضافة إلى العمليات التي يمكن للعميل تنفيذها مثل Query وMutation.

على سبيل المثال، لو أردت تصميم تطبيق موسيقي بسيط، فقد تكتب مخططاً يحتوي على النوعين Song وAuthor بهذا الشكل:

type Song {
  title: String
  author: Author
}

type Author {
  name: String
  songs: [Song]
}

ثم تضيف نوع Query لتعريف الاستعلامات المتاحة:

type Query {
  getSongs: [Song]
  getAuthors: [Author]
}

لكن بما أن هدفنا هنا هو الفهم السريع والبناء التدريجي، سنبدأ بمثال أبسط يعيد رسالة نصية فقط:

type Query {
  greeting: String
}

الآن أنشئ ملف server.js داخل مجلد server:

📁 project
├── 📁 client
└── 📁 server
    └── 📄 server.js

وفي هذا الملف نستورد الدالة gql من apollo-server لكتابة المخطط:

const { gql } = require('apollo-server');

const typeDefs = gql`
  type Query {
    greeting: String
  }
`;

تقوم gql بتحويل النص المكتوب إلى بنية يفهمها الخادم، وتُعرف هذه البنية عادة باسم Abstract Syntax Tree أو AST.

كيف تضيف Resolver لمعالجة الاستعلامات؟

بعد تعريف المخطط، نحتاج إلى تحديد الطريقة التي ستُجلب بها البيانات. هنا يأتي دور Resolver، وهو دالة مسؤولة عن إرجاع القيمة المناسبة لكل حقل في المخطط.

بما أن لدينا حقلاً واحداً فقط اسمه greeting ويعيد قيمة من النوع String، فإن دالة المعالجة ستكون مباشرة:

const resolvers = {
  Query: {
    greeting: () => 'Hello GraphQL world!👋',
  },
};

في المشاريع الحقيقية، غالباً ما تتصل هذه الدوال بقاعدة بيانات أو خدمة خارجية أو طبقة أعمال داخلية، لكن المثال الحالي يركّز على الفكرة الأساسية فقط.

إعداد الخادم باستخدام Apollo Server

الآن سنجمع بين typeDefs وresolvers داخل كائن ApolloServer ثم نبدأ تشغيل الخادم على منفذ محدد.

const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type Query {
    greeting: String
  }
`;

const resolvers = {
  Query: {
    greeting: () => 'Hello GraphQL world!👋',
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen({ port: 9000 }).then(({ url }) =>
  console.log(`Server running at ${url}`)
);

بعد حفظ الملف، شغّل الخادم بالأمر التالي:

node server/server.js

وعند نجاح التشغيل، ستظهر لك رسالة مشابهة لهذه:

~/graphql-hello-world-server > node server/server.js
Server running at http://localhost:9000/

وهنا يصبح خادم GraphQL جاهزاً لاستقبال الطلبات.

ما هي واجهة GraphQL Playground؟

عند فتح الرابط http://localhost:9000/ في المتصفح، ستظهر لك واجهة تفاعلية جاهزة تُعرف باسم GraphQL Playground. هذه الواجهة تعمل كبيئة اختبار داخل المتصفح، وتمكّنك من تنفيذ استعلامات Query وعمليات Mutation بسهولة دون الحاجة إلى أدوات خارجية.

واجهة GraphQL Playground لاختبار الاستعلامات في المتصفح

إذا كنت معتاداً على أدوات مثل Postman، فستجد أن GraphQL Playground يؤدي دوراً مشابهاً، لكنه مخصص لتجربة واجهات GraphQL بشكل أسرع وأكثر وضوحاً.

جرّب تنفيذ الاستعلام التالي في اللوحة اليسرى:

query {
  greeting
}

بعد الضغط على زر التشغيل، ستظهر النتيجة في الجهة اليمنى.

صورة متحركة توضيحية لتجربة GraphQL Playground بشكل تفاعليتنفيذ استعلام greeting داخل GraphQL Playground وعرض النتيجة

إعداد العميل Client

إنشاء ملف client.html

بعد تشغيل الخادم، ننتقل الآن إلى جزء العميل. داخل مجلد client، أنشئ ملفاً باسم client.html:

📁 project
├── 📁 client
│   └── 📄 client.html
└── 📁 server
    └── 📄 server.js

وسيحتوي الملف على بنية HTML أساسية مع عنوان مؤقت لإظهار حالة التحميل:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Hello World GraphQL Client</title>
</head>
<body>
  <h1>Loading...</h1>
  <script src="app.js"></script>
</body>
</html>

كيف تجلب البيانات من الخادم؟

إنشاء ملف app.js

في المجلد نفسه، أنشئ ملف app.js ليحتوي على منطق جلب البيانات:

📁 project
├── 📁 client
│   ├── 📄 client.html
│   └── 📄 app.js
└── 📁 server
    └── 📄 server.js

داخل هذا الملف، نحدد أولاً رابط خادم GraphQL:

const GRAPHQL_URL = 'http://localhost:9000/';

بعدها نكتب دالة غير متزامنة باسم fetchGreeting() تستخدم fetch API لإرسال طلب POST إلى الخادم:

async function fetchGreeting() {
  const response = await fetch(GRAPHQL_URL, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
    },
    body: JSON.stringify({
      query: `
        query {
          greeting
        }
      `,
    }),
  });

  const responseBody = await response.json();
  console.log(responseBody);
}

قد يبدو استخدام POST غريباً إذا كنت قادماً من عالم REST، لأن طلب قراءة البيانات هناك يُرسل غالباً باستخدام GET. لكن في GraphQL تُرسل الاستعلامات عادة داخل body، ولهذا نستخدم POST في أغلب الحالات.

بعد ذلك يمكنك استدعاء الدالة مباشرة:

const GRAPHQL_URL = 'http://localhost:9000/';

async function fetchGreeting() {
  const response = await fetch(GRAPHQL_URL, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
    },
    body: JSON.stringify({
      query: `
        query {
          greeting
        }
      `,
    }),
  });

  const responseBody = await response.json();
  console.log(responseBody);
}

fetchGreeting();

إذا فتحت الصفحة في المتصفح ثم راقبت وحدة التحكم Console داخل أدوات المطور، فسترى أن الرد يحتوي على البيانات القادمة من الخادم.

نتيجة جلب بيانات GraphQL من الخادم داخل أدوات المطور في المتصفح

كيف تعرض البيانات داخل الصفحة؟

بعد التأكد من نجاح الاتصال، نحدّث الكود بحيث يعيد فقط الكائن data بدلاً من طباعة كامل الرد.

استبدل هذا الجزء:

const responseBody = await response.json();
console.log(responseBody);

بهذا:

const { data } = await response.json();
return data;

ثم حدّث عنوان الصفحة باستخدام القيمة الموجودة في greeting:

fetchGreeting().then(({ greeting }) => {
  const title = document.querySelector('h1');
  title.textContent = greeting;
});

ليصبح ملف app.js كاملاً بالشكل التالي:

const GRAPHQL_URL = 'http://localhost:9000/';

async function fetchGreeting() {
  const response = await fetch(GRAPHQL_URL, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
    },
    body: JSON.stringify({
      query: `
        query {
          greeting
        }
      `,
    }),
  });

  const { data } = await response.json();
  return data;
}

fetchGreeting().then(({ greeting }) => {
  const title = document.querySelector('h1');
  title.textContent = greeting;
});

النتيجة النهائية هي أن النص Loading... سيتحوّل تلقائياً إلى الرسالة القادمة من الخادم.

عرض البيانات المسترجعة من خادم GraphQL داخل صفحة العميل

لماذا يُعد GraphQL مناسباً للتطبيقات الحديثة؟

  • يوفر نقطة وصول واحدة بدلاً من تعدد المسارات.
  • يسمح للعميل بطلب الحقول التي يحتاجها فقط.
  • يقلل من مشكلة الجلب الزائد أو الناقص للبيانات.
  • يسهّل تطوير تطبيقات الويب والجوال المعتمدة على البيانات.
  • يتكامل بسلاسة مع أدوات قوية مثل Apollo Server وApollo Client.

ملاحظات عملية للمبتدئين

  1. ابدأ بمخطط صغير جداً قبل الانتقال إلى العلاقات المعقدة بين الأنواع.
  2. احرص على مطابقة كل Resolver للتعريف الموجود في Schema.
  3. اختبر الاستعلامات أولاً عبر GraphQL Playground قبل ربطها بالواجهة.
  4. نظّم المشروع من البداية إلى مجلدات واضحة مثل client وserver.
  5. عند بناء مشروع أكبر، افصل الأنواع والاستعلامات والمحللات في ملفات مستقلة لسهولة الصيانة.

مصادر مفيدة للتوسع في GraphQL

  • الموقع الرسمي لـ GraphQL لفهم المفاهيم الأساسية والمتقدمة.
  • توثيق Apollo للتعمق في إعداد الخوادم والعملاء.
  • أدوات مثل GraphQL Playground وامتدادات VSCode لتحسين تجربة التطوير.
  • دورات تطبيقية تركّز على بناء تطبيقات كاملة باستخدام Node.js وReact.

الخلاصة التقنية

يمنحك الجمع بين GraphQL وNode.js بداية قوية لبناء واجهات برمجة تطبيقات مرنة وسهلة التطوير. هذا المثال البسيط يوضح الفكرة الجوهرية: تعريف مخطط واضح، ربطه بدوال Resolver، ثم استهلاك البيانات من جهة العميل بكفاءة. تقنياً، يُعد هذا الأسلوب مناسباً جداً عندما تحتاج التطبيقات إلى تحكم دقيق في البيانات المعروضة وتقليل عدد الطلبات والتعقيد في الواجهة الخلفية.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *