بناء تطبيق Yelp متكامل باستخدام React و GraphQL (إصدار عالم Dune)

دقائق القراءة: 14
لقد قالها فرانك هربرت في روايته الخالدة “كثيب” (Dune): “لا يجب أن أخاف. الخوف قاتل العقل. الخوف هو الموت الصغير الذي يجلب الفناء التام. سأواجه خوفي. سأسمح له بالمرور فوقي ومن خلالي. وعندما يذهب، لن يبقى شيء. سأبقى أنا وحدي.”

قد تتساءل: “ما علاقة الخوف بتطبيق React؟” أولاً، لا يوجد ما يدعو للخوف في تطبيقات React. في الواقع، في هذا التطبيق تحديداً، حظرنا الخوف! أليس هذا رائعاً؟

الآن بعد أن أصبحت مستعداً لتكون بلا خوف، دعنا نناقش تطبيقنا. إنه نسخة مصغرة من Yelp، حيث يقوم المستخدمون بمراجعة الكواكب من سلسلة الخيال العلمي الكلاسيكية “كثيب” (Dune) بدلاً من مراجعة المطاعم. (لماذا؟ لأن هناك فيلماً جديداً قادماً لـ Dune… لكن دعنا نعود إلى النقطة الأساسية).

لبناء تطبيقنا المتكامل (Full-Stack)، سنستخدم تقنيات تجعل حياتنا أسهل بكثير:

  • React: إطار عمل واجهة أمامية بديهي وقابل للتركيب، لأن أدمغتنا تحب تجميع الأشياء.
  • GraphQL: ربما سمعت العديد من الأسباب التي تجعل GraphQL رائعاً. حتى الآن، الأهم هو إنتاجية المطور وسعادته.
  • Hasura: قم بإعداد واجهة برمجة تطبيقات GraphQL API مُنشأة تلقائياً فوق قاعدة بيانات Postgres في أقل من 30 ثانية.
  • Heroku: لاستضافة قاعدة بياناتنا.

وكيف يمنحني GraphQL السعادة؟ أرى أنك متشكك. لكنك على الأرجح ستقتنع بمجرد أن تقضي بعض الوقت مع GraphiQL (ساحة لعب GraphQL). استخدام GraphQL سهل للغاية لمطور الواجهة الأمامية، مقارنة بالطرق القديمة لنقاط نهاية REST المعقدة.

يمنحك GraphQL نقطة نهاية واحدة تستمع إلى جميع مشاكلك… أقصد استعلاماتك. إنه مستمع رائع لدرجة أنه يمكنك أن تخبره بالضبط ما تريده، وسيقدمه لك، لا أقل ولا أكثر. هل تشعر بالحماس لهذه التجربة العلاجية؟ دعنا نتعمق في هذا الدليل حتى تتمكن من تجربته في أقرب وقت ممكن! إليك المستودع (repo) إذا كنت ترغب في متابعة كتابة الكود.

الجزء الأول: البحث عن الكواكب

الخطوة 1: النشر على Heroku

الخطوة الأولى في كل رحلة جيدة هي الجلوس مع بعض الشاي الساخن وتناوله بهدوء. بمجرد الانتهاء من ذلك، يمكننا النشر على Heroku من موقع Hasura الإلكتروني. سيؤدي هذا إلى إعداد كل ما نحتاجه: قاعدة بيانات Postgres، ومحرك Hasura GraphQL الخاص بنا، وبعض الوجبات الخفيفة للرحلة.
واجهة Hasura Console أثناء عملية النشر على Heroku

الخطوة 2: إنشاء جدول الكواكب

يرغب مستخدمونا في مراجعة الكواكب. لذلك سنقوم بإنشاء جدول Postgres عبر وحدة تحكم Hasura لتخزين بيانات الكواكب الخاصة بنا. ومن الجدير بالذكر الكوكب الشرير Giedi Prime، الذي كان يجذب الانتباه بمأكولاته غير التقليدية.
إنشاء جدول planets (الكواكب) في Hasura Console

في هذه الأثناء في تبويب GraphiQL: قامت Hasura بإنشاء مخطط GraphQL الخاص بنا تلقائياً! يمكنك اللعب به في “المستكشف” (Explorer) هنا.
مخطط GraphQL (Schema) المُنشأ تلقائياً في GraphiQL

الخطوة 3: إنشاء تطبيق React

سنحتاج إلى واجهة مستخدم لتطبيقنا، لذلك سنقوم بإنشاء تطبيق React وتثبيت بعض المكتبات لطلبات GraphQL، والتوجيه (routing)، والأنماط (styles). (تأكد من تثبيت Node.js أولاً).

> npx create-react-app melange
> cd melange
> npm install graphql @apollo/client react-router-dom @emotion/styled @emotion/core
> npm start

الخطوة 4: إعداد عميل Apollo

سيساعدنا Apollo Client في طلبات شبكة GraphQL والتخزين المؤقت (caching)، حتى نتمكن من تجنب كل هذا العمل الشاق. سنقوم أيضاً بإجراء أول استعلام لنا وسرد كواكبنا! بدأ تطبيقنا يتشكل.

import React from "react";
import { render } from "react-dom";
import { ApolloProvider } from "@apollo/client";
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import Planets from "./components/Planets";

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: "[YOUR HASURA GRAPHQL ENDPOINT]",
  }),
});

const App = () => (
  <ApolloProvider client={client}>
    <Planets />
  </ApolloProvider>
);

render(
  <App />,
  document.getElementById("root")
);

نختبر استعلام GraphQL الخاص بنا في وحدة تحكم Hasura قبل نسخه ولصقه في الكود الخاص بنا.
اختبار استعلام GraphQL في Hasura Console قبل دمجه في الكود

import React from "react";
import { useQuery, gql } from "@apollo/client";

const PLANETS = gql`
  {
    planets {
      id
      name
      cuisine
    }
  }
`;

const Planets = ({ newPlanets }) => {
  const { loading, error, data } = useQuery(PLANETS);

  if (loading) return <p>Loading ...</p>;
  if (error) return <p>Error :(</p>;

  return data.planets.map(({ id, name, cuisine }) => (
    <div key={id}>
      <p>{name} | {cuisine}</p>
    </div>
  ));
};

export default Planets;

الخطوة 5: تنسيق القائمة

قائمة كواكبنا لطيفة وكل شيء، لكنها تحتاج إلى لمسة جمالية باستخدام Emotion (راجع المستودع للأنماط الكاملة).
قائمة الكواكب بعد تطبيق التنسيقات باستخدام Emotion

الخطوة 6: نموذج البحث والحالة

يرغب مستخدمونا في البحث عن الكواكب وترتيبها حسب الاسم. لذلك سنضيف نموذج بحث يستعلم نقطة النهاية الخاصة بنا بسلسلة بحث، ونمرر النتائج إلى مكون Planets لتحديث قائمة الكواكب لدينا. كما نستخدم React Hooks لإدارة حالة تطبيقنا.

import React, { useState } from "react";
import { useLazyQuery, gql } from "@apollo/client";
import Search from "./Search";
import Planets from "./Planets";

const SEARCH = gql`
  query Search($match: String) {
    planets(order_by: { name: asc }, where: { name: { _ilike: $match } }) {
      name
      cuisine
      id
    }
  }
`;

const PlanetSearch = () => {
  const [inputVal, setInputVal] = useState("");
  const [search, { loading, error, data }] = useLazyQuery(SEARCH);

  return (
    <div>
      <Search
        inputVal={inputVal}
        onChange={(e) => setInputVal(e.target.value)}
        onSearch={() => search({ variables: { match: `%${inputVal}%` } })}
      />
      <Planets newPlanets={data ? data.planets : null} />
    </div>
  );
};

export default PlanetSearch;

import React from "react";
import { useQuery, gql } from "@apollo/client";
import { List, ListItem } from "./shared/List";
import { Badge } from "./shared/Badge";

const PLANETS = gql`
  {
    planets {
      id
      name
      cuisine
    }
  }
`;

const Planets = ({ newPlanets }) => {
  const { loading, error, data } = useQuery(PLANETS);

  const renderPlanets = (planets) => {
    return planets.map(({ id, name, cuisine }) => (
      <ListItem key={id}>
        {name} <Badge>{cuisine}</Badge>
      </ListItem>
    ));
  };

  if (loading) return <p>Loading ...</p>;
  if (error) return <p>Error :(</p>;

  return <List>{renderPlanets(newPlanets || data.planets)}</List>;
};

export default Planets;

import React from "react";
import styled from "@emotion/styled";
import { Input, Button } from "./shared/Form";

const SearchForm = styled.div`
  display: flex;
  align-items: center;
  > button {
    margin-left: 1rem;
  }
`;

const Search = ({ inputVal, onChange, onSearch }) => {
  return (
    <SearchForm>
      <Input value={inputVal} onChange={onChange} />
      <Button onClick={onSearch}>Search</Button>
    </SearchForm>
  );
};

export default Search;

import React from "react";
import { render } from "react-dom";
import { ApolloProvider } from "@apollo/client";
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import PlanetSearch from "./components/PlanetSearch";
import Logo from "./components/shared/Logo";
import "./index.css";

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: "[YOUR HASURA GRAPHQL ENDPOINT]",
  }),
});

const App = () => (
  <ApolloProvider client={client}>
    <Logo />
    <PlanetSearch />
  </ApolloProvider>
);

render(
  <App />,
  document.getElementById("root")
);

الخطوة 7: احتفل بالإنجاز

لقد قمنا بالفعل بتنفيذ قائمة الكواكب وميزات البحث! ننظر بمحبة إلى عملنا اليدوي، ونلتقط بعض صور السيلفي معاً، ثم ننتقل إلى المراجعات.
واجهة المستخدم لتطبيق الكواكب بعد إضافة ميزة البحث

الجزء الثاني: المراجعات المباشرة

الخطوة 1: إنشاء جدول المراجعات

سيزور مستخدمونا هذه الكواكب، ويكتبون مراجعات حول تجربتهم. سنقوم بإنشاء جدول عبر وحدة تحكم Hasura لبيانات المراجعة الخاصة بنا.
إنشاء جدول reviews (المراجعات) في Hasura Console

نضيف مفتاحاً أجنبياً (foreign key) من عمود planet_id إلى عمود id في جدول planets، للإشارة إلى أن قيم planet_id في جدول reviews يجب أن تتطابق مع قيم id في جدول planets.
إضافة مفتاح خارجي (Foreign Key) لربط جدول المراجعات بالكواكب

الخطوة 2: تتبع العلاقات

لكل كوكب مراجعات متعددة، بينما كل مراجعة تخص كوكباً واحداً: إنها علاقة واحد إلى متعدد (one-to-many relationship). نقوم بإنشاء وتتبع هذه العلاقة عبر وحدة تحكم Hasura، حتى يمكن عرضها في مخطط GraphQL الخاص بنا.
تتبع العلاقة بين جدول الكواكب والمراجعات في Hasura Console

الآن يمكننا استعلام المراجعات لكل كوكب في “المستكشف” (Explorer)!
استعلام عن المراجعات لكل كوكب باستخدام GraphQL Explorer

الخطوة 3: إعداد التوجيه (Routing)

نريد أن نتمكن من النقر على كوكب وعرض مراجعاته في صفحة منفصلة. سنقوم بإعداد التوجيه باستخدام React Router، وسرد المراجعات في صفحة الكوكب.

import React from "react";
import { render } from "react-dom";
import { ApolloProvider } from "@apollo/client";
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import PlanetSearch from "./components/PlanetSearch";
import Planet from "./components/Planet";
import Logo from "./components/shared/Logo";
import "./index.css";

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: "[YOUR HASURA GRAPHQL ENDPOINT]",
  }),
});

const App = () => (
  <BrowserRouter>
    <ApolloProvider client={client}>
      <Logo />
      <Switch>
        <Route path="/planet/:id" component={Planet} />
        <Route path="/" component={PlanetSearch} />
      </Switch>
    </ApolloProvider>
  </BrowserRouter>
);

render(
  <App />,
  document.getElementById("root")
);

import React from "react";
import { useQuery, gql } from "@apollo/client";
import { List, ListItem } from "./shared/List";
import { Badge } from "./shared/Badge";

const PLANET = gql`
  query Planet($id: uuid!) {
    planets_by_pk(id: $id) {
      id
      name
      cuisine
      reviews {
        id
        body
      }
    }
  }
`;

const Planet = ({
  match: {
    params: {
      id
    },
  },
}) => {
  const { loading, error, data } = useQuery(PLANET, {
    variables: { id },
  });

  if (loading) return <p>Loading ...</p>;
  if (error) return <p>Error :(</p>;

  const { name, cuisine, reviews } = data.planets_by_pk;

  return (
    <div>
      <h3>
        {name} <Badge>{cuisine}</Badge>
      </h3>
      <List>
        {reviews.map((review) => (
          <ListItem key={review.id}>
            {review.body}
          </ListItem>
        ))}
      </List>
    </div>
  );
};

export default Planet;

import React from "react";
import { useQuery, gql } from "@apollo/client";
import { Link } from "react-router-dom";
import { List, ListItemWithLink } from "./shared/List";
import { Badge } from "./shared/Badge";

const PLANETS = gql`
  {
    planets {
      id
      name
      cuisine
    }
  }
`;

const Planets = ({ newPlanets }) => {
  const { loading, error, data } = useQuery(PLANETS);

  const renderPlanets = (planets) => {
    return planets.map(({ id, name, cuisine }) => (
      <ListItemWithLink key={id}>
        <Link to={`/planet/${id}`}>
          {name} <Badge>{cuisine}</Badge>
        </Link>
      </ListItemWithLink>
    ));
  };

  if (loading) return <p>Loading ...</p>;
  if (error) return <p>Error :(</p>;

  return <List>{renderPlanets(newPlanets || data.planets)}</List>;
};

export default Planets;

الخطوة 4: إعداد الاشتراكات (Subscriptions)

نقوم بتثبيت مكتبات جديدة وإعداد Apollo Client لدعم الاشتراكات. ثم، نغير استعلام المراجعات الخاص بنا إلى اشتراك حتى يتمكن من عرض التحديثات المباشرة.

> npm install @apollo/link-ws subscriptions-transport-ws

import React from "react";
import { render } from "react-dom";
import {
  ApolloProvider,
  ApolloClient,
  HttpLink,
  InMemoryCache,
  split,
} from "@apollo/client";
import { getMainDefinition } from "@apollo/client/utilities";
import { WebSocketLink } from "@apollo/link-ws";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import PlanetSearch from "./components/PlanetSearch";
import Planet from "./components/Planet";
import Logo from "./components/shared/Logo";
import "./index.css";

const GRAPHQL_ENDPOINT = "[YOUR HASURA GRAPHQL ENDPOINT]";

const httpLink = new HttpLink({
  uri: `https://${GRAPHQL_ENDPOINT}`,
});

const wsLink = new WebSocketLink({
  uri: `ws://${GRAPHQL_ENDPOINT}`,
  options: {
    reconnect: true,
  },
});

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === "OperationDefinition" &&
      definition.operation === "subscription"
    );
  },
  wsLink,
  httpLink
);

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: splitLink,
});

const App = () => (
  <BrowserRouter>
    <ApolloProvider client={client}>
      <Logo />
      <Switch>
        <Route path="/planet/:id" component={Planet} />
        <Route path="/" component={PlanetSearch} />
      </Switch>
    </ApolloProvider>
  </BrowserRouter>
);

render(
  <App />,
  document.getElementById("root")
);

import React from "react";
import { useSubscription, gql } from "@apollo/client";
import { List, ListItem } from "./shared/List";
import { Badge } from "./shared/Badge";

const PLANET = gql`
  subscription Planet($id: uuid!) {
    planets_by_pk(id: $id) {
      id
      name
      cuisine
      reviews {
        id
        body
      }
    }
  }
`;

const Planet = ({
  match: {
    params: {
      id
    },
  },
}) => {
  const { loading, error, data } = useSubscription(PLANET, {
    variables: { id },
  });

  if (loading) return <p>Loading ...</p>;
  if (error) return <p>Error :(</p>;

  const { name, cuisine, reviews } = data.planets_by_pk;

  return (
    <div>
      <h3>
        {name} <Badge>{cuisine}</Badge>
      </h3>
      <List>
        {reviews.map((review) => (
          <ListItem key={review.id}>
            {review.body}
          </ListItem>
        ))}
      </List>
    </div>
  );
};

export default Planet;

واجهة المستخدم لعرض مراجعات الكواكب المباشرة

الخطوة 5: رقصة دودة الرمل

لقد قمنا بتنفيذ الكواكب مع المراجعات المباشرة! قم ببعض الرقص للاحتفال قبل الشروع في العمل الجاد.
صورة متحركة لدودة الرمل من فيلم Dune تعبيراً عن الاحتفال

الجزء الثالث: منطق الأعمال

الخطوة 1: إضافة نموذج الإدخال

نريد طريقة لإرسال المراجعات عبر واجهة المستخدم الخاصة بنا. سنعيد تسمية نموذج البحث الخاص بنا ليصبح InputForm عاماً ونضيفه فوق قائمة المراجعات.

import React, { useState } from "react";
import { useSubscription, gql } from "@apollo/client";
import { List, ListItem } from "./shared/List";
import { Badge } from "./shared/Badge";
import InputForm from "./shared/InputForm";

const PLANET = gql`
  subscription Planet($id: uuid!) {
    planets_by_pk(id: $id) {
      id
      name
      cuisine
      reviews(order_by: { created_at: desc }) {
        id
        body
        created_at
      }
    }
  }
`;

const Planet = ({
  match: {
    params: {
      id
    },
  },
}) => {
  const [inputVal, setInputVal] = useState("");
  const { loading, error, data } = useSubscription(PLANET, {
    variables: { id },
  });

  if (loading) return <p>Loading ...</p>;
  if (error) return <p>Error :(</p>;

  const { name, cuisine, reviews } = data.planets_by_pk;

  return (
    <div>
      <h3>
        {name} <Badge>{cuisine}</Badge>
      </h3>
      <InputForm
        inputVal={inputVal}
        onChange={(e) => setInputVal(e.target.value)}
        onSubmit={() => {}}
        buttonText="Submit"
      />
      <List>
        {reviews.map((review) => (
          <ListItem key={review.id}>
            {review.body}
          </ListItem>
        ))}
      </List>
    </div>
  );
};

export default Planet;

الخطوة 2: اختبار طفرة (Mutation) المراجعة

سنستخدم طفرة لإضافة مراجعات جديدة. نختبر طفرتنا باستخدام GraphiQL في وحدة تحكم Hasura.
اختبار طفرة (Mutation) لإضافة مراجعة جديدة في GraphiQL

ونقوم بتحويلها لقبول المتغيرات حتى نتمكن من استخدامها في الكود الخاص بنا.
تحويل طفرة GraphQL لقبول المتغيرات

الخطوة 3: إنشاء إجراء (Action)

لقد طلبت جماعة Bene Gesserit منا عدم السماح (أو بالأحرى، فرض رقابة على) كلمة “fear” (الخوف) في المراجعات. سنقوم بإنشاء إجراء لمنطق الأعمال الذي سيتحقق من هذه الكلمة كلما أرسل المستخدم مراجعة.
إنشاء إجراء (Action) جديد في Hasura Console

داخل الإجراء الجديد الخاص بنا، نذهب إلى تبويب “إنشاء الكود” (Codegen).
تبويب Codegen في إعدادات إجراء Hasura

نختار خيار nodejs-express، وننسخ كود المعالج (handler) الجاهز أدناه.
اختيار خيار nodejs-express لإنشاء كود الإجراء

نضغط على “جرب على Glitch“، والذي يأخذنا إلى تطبيق express بسيط، حيث يمكننا لصق كود المعالج الخاص بنا.
واجهة Glitch لإنشاء تطبيق Express بسيط

بالعودة إلى الإجراء الخاص بنا، نحدد عنوان URL للمعالج الخاص بنا ليكون هو نفسه من تطبيق Glitch، مع المسار الصحيح من كود المعالج الخاص بنا.
تعيين عنوان URL لمعالج الإجراء في Hasura

يمكننا الآن اختبار الإجراء الخاص بنا في وحدة التحكم. يعمل كطفرة عادية، لأننا لم نقم بعد بإضافة أي منطق أعمال للتحقق من كلمة “fear”.
اختبار الإجراء في Hasura Console قبل إضافة منطق الأعمال

الخطوة 4: إضافة منطق الأعمال

في المعالج الخاص بنا، نضيف منطق أعمال يتحقق من كلمة “fear” داخل نص المراجعة. إذا كانت المراجعة خالية من الخوف، فإننا ننفذ الطفرة كالمعتاد. وإلا، فإننا نعيد خطأً مشؤوماً.
إضافة منطق الأعمال للتحقق من كلمة 'fear' في مراجعات Glitch

إذا قمنا بتشغيل الإجراء بكلمة “fear” الآن، سنحصل على الخطأ في الاستجابة:
رسالة خطأ عند محاولة إرسال مراجعة تحتوي على كلمة 'fear'

الخطوة 5: ترتيب المراجعات

ترتيب مراجعاتنا حالياً فوضوي. نضيف عمود created_at إلى جدول reviews حتى نتمكن من الترتيب حسب الأحدث أولاً.

reviews(order_by: { created_at : desc })

الخطوة 6: إضافة طفرة المراجعة

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

import React, { useState } from "react";
import { useSubscription, useMutation, gql } from "@apollo/client";
import { List, ListItem } from "./shared/List";
import { Badge } from "./shared/Badge";
import InputForm from "./shared/InputForm";

const PLANET = gql`
  subscription Planet($id: uuid!) {
    planets_by_pk(id: $id) {
      id
      name
      cuisine
      reviews(order_by: { created_at: desc }) {
        id
        body
        created_at
      }
    }
  }
`;

const ADD_REVIEW = gql`
  mutation($body: String!, $id: uuid!) {
    AddFearlessReview(body: $body, id: $id) {
      affected_rows
    }
  }
`;

const Planet = ({
  match: {
    params: {
      id
    },
  },
}) => {
  const [inputVal, setInputVal] = useState("");
  const { loading, error, data } = useSubscription(PLANET, {
    variables: { id },
  });
  const [addReview] = useMutation(ADD_REVIEW);

  if (loading) return <p>Loading ...</p>;
  if (error) return <p>Error :(</p>;

  const { name, cuisine, reviews } = data.planets_by_pk;

  return (
    <div>
      <h3>
        {name} <Badge>{cuisine}</Badge>
      </h3>
      <InputForm
        inputVal={inputVal}
        onChange={(e) => setInputVal(e.target.value)}
        onSubmit={() => {
          addReview({ variables: { id, body: inputVal } })
            .then(() => setInputVal(""))
            .catch((e) => {
              setInputVal(e.message);
            });
        }}
        buttonText="Submit"
      />
      <List>
        {reviews.map((review) => (
          <ListItem key={review.id}>
            {review.body}
          </ListItem>
        ))}
      </List>
    </div>
  );
};

export default Planet;

إذا أرسلنا مراجعة جديدة تتضمن كلمة “fear” الآن، فسنحصل على خطأنا المشؤوم، والذي نعرضه في حقل الإدخال.
اختبار واجهة المستخدم النهائية: إظهار رسالة الخطأ عند إرسال مراجعة محظورة

الخطوة 7: لقد نجحنا!

تهانينا على بناء تطبيق React و GraphQL متكامل!
صورة متحركة لشخصين يتبادلان التحية (High Five) احتفالاً بالإنجاز

ماذا يخبئ المستقبل؟
صورة فنية مستوحاة من عالم Dune

لو كان لدينا بعض “الميلانج” (spice melange)، لكنا عرفنا. لكننا بنينا العديد من الميزات في وقت قصير جداً! لقد غطينا استعلامات GraphQL (queries)، والطفرات (mutations)، والاشتراكات (subscriptions)، والتوجيه (routing)، والبحث، وحتى منطق الأعمال المخصص باستخدام إجراءات Hasura (Hasura actions)! آمل أن تكون قد استمتعت بالبرمجة والمتابعة.

ما هي الميزات الأخرى التي ترغب في رؤيتها في هذا التطبيق؟ تواصل معي على Twitter، وسأقوم بإنشاء المزيد من الدروس التعليمية! إذا كنت مستوحى لإضافة ميزات بنفسك، يرجى المشاركة – أحب أن أسمع عنها.

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

يمثل هذا الدليل الشامل نموذجاً ممتازاً لكيفية تسريع عملية تطوير تطبيقات الويب المتكاملة باستخدام مجموعة تقنيات حديثة وقوية. يبرز استخدام React كواجهة أمامية تفاعلية ومرنة، بينما يوفر GraphQL حلاً فعالاً ومرناً لجلب البيانات ومعالجتها، متفوقاً على تعقيدات REST APIs التقليدية. إن دمج Hasura مع قاعدة بيانات Postgres يمثل نقطة تحول حقيقية، حيث يقوم بتوليد GraphQL API تلقائياً، مما يقلل بشكل كبير من وقت إعداد الواجهة الخلفية ويسمح للمطورين بالتركيز على منطق الأعمال الأساسي وتجربة المستخدم. كما أن قدرة Hasura على دعم الاشتراكات (subscriptions) تتيح بناء ميزات في الوقت الفعلي بسهولة، مما يعزز تفاعل المستخدم. إضافة منطق الأعمال المخصص عبر إجراءات Hasura يوفر مرونة كبيرة لتطبيق قواعد عمل معقدة دون الحاجة إلى بنية خلفية مخصصة بالكامل. هذا النهج يقلل من التعقيد ويزيد من إنتاجية المطور، مما يجعله خياراً مثالياً لتطوير تطبيقات ويب سريعة وذات ميزات غنية.

اترك تعليقاً

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