واجهات GraphQL

أساسيات Apollo Client

18 دقيقة الدرس 19 من 35

البدء مع Apollo Client

Apollo Client هو مكتبة شاملة لإدارة الحالة لـ JavaScript تمكنك من إدارة البيانات المحلية والبعيدة باستخدام GraphQL. إنه عميل GraphQL الأكثر شيوعًا لتطبيقات React وVue وAngular وJavaScript الفانيليا.

ما هو Apollo Client؟

يوفر Apollo Client التخزين المؤقت الذكي وجلب البيانات التصريحي وأدوات تطوير قوية. يتعامل مع طلبات الشبكة والتخزين المؤقت ومعالجة الأخطاء وتحديثات واجهة المستخدم المتفائلة تلقائيًا.

الميزات الرئيسية:
  • InMemoryCache: التخزين المؤقت المطبّع لتخزين البيانات بكفاءة
  • جلب البيانات التصريحي: خطافات React مثل useQuery وuseMutation
  • التحديثات التلقائية: تحديثات واجهة المستخدم عند تغيير ذاكرة التخزين المؤقت
  • معالجة الأخطاء: حالات أخطاء مدمجة ومنطق إعادة المحاولة
  • أدوات التطوير: امتداد المتصفح لتصحيح الاستعلامات وذاكرة التخزين المؤقت
  • دعم TypeScript: أمان كامل للنوع مع أنواع مُنشأة

التثبيت والإعداد

تثبيت التبعيات:
# تثبيت Apollo Client وGraphQL
npm install @apollo/client graphql

# لتطبيقات React
npm install @apollo/client graphql react

إنشاء مثيل Apollo Client

قم بإعداد Apollo Client مع نقطة نهاية GraphQL وتكوين ذاكرة التخزين المؤقت.

إعداد Apollo Client الأساسي:
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://api.example.com/graphql',
  cache: new InMemoryCache()
});

export default client;
إعداد متقدم مع المصادقة:
import {
  ApolloClient,
  InMemoryCache,
  HttpLink,
  ApolloLink
} from '@apollo/client';

// اتصال HTTP بواجهة برمجة التطبيقات
const httpLink = new HttpLink({
  uri: 'https://api.example.com/graphql'
});

// وسيط لإضافة رمز المصادقة إلى الرؤوس
const authLink = new ApolloLink((operation, forward) => {
  const token = localStorage.getItem('auth_token');

  operation.setContext({
    headers: {
      authorization: token ? `Bearer ${token}` : ''
    }
  });

  return forward(operation);
});

// إنشاء عميل مع المصادقة وذاكرة التخزين المؤقت
const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
  defaultOptions: {
    watchQuery: {
      fetchPolicy: 'cache-and-network'
    }
  }
});

export default client;

ApolloProvider - توفير العميل لـ React

قم بتغليف تطبيق React الخاص بك بـ ApolloProvider لجعل العميل متاحًا في جميع أنحاء شجرة المكونات.

إعداد التطبيق مع ApolloProvider:
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './apollo-client';
import UserList from './components/UserList';

function App() {
  return (
    <ApolloProvider client={client}>
      <div className="App">
        <h1>تطبيق GraphQL الخاص بي</h1>
        <UserList />
      </div>
    </ApolloProvider>
  );
}

export default App;

خطاف useQuery - جلب البيانات

ينفذ خطاف useQuery استعلامات GraphQL ويعيد حالات التحميل والخطأ والبيانات.

مثال useQuery الأساسي:
import React from 'react';
import { useQuery, gql } from '@apollo/client';

const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
      avatar
    }
  }
`;

function UserList() {
  const { loading, error, data } = useQuery(GET_USERS);

  if (loading) return <p>جارٍ تحميل المستخدمين...</p>;
  if (error) return <p>خطأ: {error.message}</p>;

  return (
    <div>
      <h2>المستخدمون</h2>
      <ul>
        {data.users.map(user => (
          <li key={user.id}>
            <img src={user.avatar} alt={user.name} />
            <strong>{user.name}</strong> - {user.email}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default UserList;

متغيرات الاستعلام

مرر متغيرات ديناميكية إلى استعلاماتك للتصفية والمعلمات.

useQuery مع المتغيرات:
import React from 'react';
import { useQuery, gql } from '@apollo/client';

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      posts {
        id
        title
      }
    }
  }
`;

function UserProfile({ userId }) {
  const { loading, error, data } = useQuery(GET_USER, {
    variables: { id: userId }
  });

  if (loading) return <p>جارٍ التحميل...</p>;
  if (error) return <p>خطأ: {error.message}</p>;

  const { user } = data;

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <h3>المنشورات</h3>
      <ul>
        {user.posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

export default UserProfile;

إعادة الجلب والاستطلاع

أعد جلب الاستعلامات يدويًا أو قم بإعداد استطلاع تلقائي للتحديثات في الوقت الفعلي.

مثال إعادة الجلب والاستطلاع:
import React from 'react';
import { useQuery, gql } from '@apollo/client';

const GET_STATS = gql`
  query GetStats {
    stats {
      users
      posts
      comments
      lastUpdated
    }
  }
`;

function Dashboard() {
  const { loading, error, data, refetch } = useQuery(GET_STATS, {
    pollInterval: 5000 // الاستطلاع كل 5 ثوانٍ
  });

  if (loading) return <p>جارٍ تحميل الإحصائيات...</p>;
  if (error) return <p>خطأ: {error.message}</p>;

  return (
    <div>
      <h2>لوحة المعلومات</h2>
      <div>المستخدمون: {data.stats.users}</div>
      <div>المنشورات: {data.stats.posts}</div>
      <div>التعليقات: {data.stats.comments}</div>
      <div>آخر تحديث: {data.stats.lastUpdated}</div>

      <button onClick={() => refetch()}>
        تحديث الإحصائيات
      </button>
    </div>
  );
}

export default Dashboard;

سياسات جلب الشبكة

يوفر Apollo Client سياسات جلب مختلفة للتحكم في كيفية جلب البيانات وتخزينها مؤقتًا.

سياسات الجلب:
// cache-first (افتراضي): التحقق من ذاكرة التخزين المؤقت أولاً، الجلب إذا لم يتم العثور عليه
const { data } = useQuery(QUERY, {
  fetchPolicy: 'cache-first'
});

// cache-and-network: إرجاع البيانات المخزنة مؤقتًا، ثم الجلب من الشبكة
const { data } = useQuery(QUERY, {
  fetchPolicy: 'cache-and-network'
});

// network-only: الجلب دائمًا من الشبكة، تحديث ذاكرة التخزين المؤقت
const { data } = useQuery(QUERY, {
  fetchPolicy: 'network-only'
});

// no-cache: الجلب دائمًا، لا تخزين النتيجة مؤقتًا
const { data } = useQuery(QUERY, {
  fetchPolicy: 'no-cache'
});

// cache-only: إرجاع البيانات المخزنة مؤقتًا فقط، عدم الجلب أبدًا
const { data } = useQuery(QUERY, {
  fetchPolicy: 'cache-only'
});
أفضل ممارسات سياسة الجلب:
  • استخدم cache-first للبيانات الثابتة التي نادرًا ما تتغير
  • استخدم cache-and-network للبيانات التي تتحدث بشكل متكرر
  • استخدم network-only للمصادقة أو البيانات الحرجة في الوقت الفعلي
  • استخدم no-cache للبيانات الحساسة التي لا ينبغي تخزينها مؤقتًا

معالجة الأخطاء

معالجة أخطاء شاملة:
import React from 'react';
import { useQuery, gql } from '@apollo/client';

const GET_POSTS = gql`
  query GetPosts {
    posts {
      id
      title
    }
  }
`;

function Posts() {
  const { loading, error, data } = useQuery(GET_POSTS, {
    onError: (error) => {
      console.error('خطأ في الاستعلام:', error);
      // تسجيل في خدمة تتبع الأخطاء
    },
    onCompleted: (data) => {
      console.log('اكتمل الاستعلام:', data);
    }
  });

  if (loading) return <p>جارٍ التحميل...</p>;

  if (error) {
    // خطأ في الشبكة
    if (error.networkError) {
      return <p>خطأ في الشبكة. يرجى التحقق من اتصالك.</p>;
    }

    // أخطاء GraphQL
    if (error.graphQLErrors.length > 0) {
      return (
        <div>
          {error.graphQLErrors.map((err, i) => (
            <p key={i}>خطأ: {err.message}</p>
          ))}
        </div>
      );
    }

    return <p>حدث خطأ ما.</p>;
  }

  return (
    <ul>
      {data.posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

export default Posts;
تحذير: تعامل دائمًا مع حالات التحميل والخطأ في مكوناتك. يمكن أن تؤدي الأخطاء غير المعالجة إلى تعطل تطبيقك أو ترك المستخدمين يحدقون في شاشات فارغة.
تمرين:
  1. قم بإعداد Apollo Client في تطبيق React مع رؤوس المصادقة
  2. أنشئ مكونًا يجلب قائمة بمنشورات المدونة باستخدام useQuery
  3. أضف زر إعادة جلب يحدث البيانات يدويًا
  4. نفذ حالات تحميل وخطأ مناسبة مع رسائل سهلة الاستخدام
  5. جرب سياسات جلب مختلفة (cache-first، cache-and-network، network-only) ولاحظ السلوك