في المشهد المتطور باستمرار لتطوير الويب، برزت GraphQL
كبديل قوي لواجهات برمجة التطبيقات التقليدية (REST APIs
)، مقدمة للعملاء القدرة على طلب البيانات التي يحتاجونها بالضبط. ومع ذلك، يمكن لهذه المرونة أن تقدم مجموعة جديدة من التحديات، خاصة عندما يتعلق الأمر بالحفاظ على سلامة الأنواع (type safety
) بين الواجهة الأمامية (frontend
) والخلفية (backend
). هنا يأتي دور graphql-codegen
، أداة ثورية تعمل على أتمتة توليد الكود المكتوب بأنواع (typed code
) من مخطط GraphQL
الخاص بك، مما يعزز سير عمل التطوير الخاص بك ويقضي على فئة كاملة من أخطاء وقت التشغيل (runtime errors
).
سيكون هذا المقال دليلك لفهم وإتقان graphql-codegen
. سنبدأ بالمفاهيم الأساسية، ونتطرق إلى مثال عملي خطوة بخطوة لإعداد الأداة وتكوينها، ونستكشف أفضل الممارسات لدمجها في مشاريعك. بنهاية هذا الدليل، ستكون مجهزًا للاستفادة من graphql-codegen
لبناء تطبيقات أكثر قوة وقابلية للصيانة وسلامة في الأنواع (type-safe
).
هل تريد منصة متكاملة وشاملة لفريق المطورين الخاص بك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر أقل بكثير!
ما هو graphql-codegen
ولماذا تحتاجه؟
في جوهره، graphql-codegen
هو أداة سطر أوامر تقوم بفحص مخطط GraphQL
الخاص بك وعمليات GraphQL
الخاصة بك (الاستعلامات queries
، الطفرات mutations
، والاشتراكات subscriptions
) وتوليد كود بمجموعة متنوعة من اللغات. لغرض هذا الدليل للمبتدئين، سنركز على حالة الاستخدام الأكثر شيوعًا: توليد أنواع TypeScript
وخطافات (hooks
) لتطبيقات الواجهة الأمامية.
المشكلة الأساسية التي يحلها graphql-codegen
هي عملية كتابة واجهات TypeScript
يدويًا لهياكل بيانات واجهة برمجة تطبيقات GraphQL
الخاصة بك ونتائج استعلاماتك، وهي عملية مملة وعرضة للأخطاء. بدونه، يُترك المطورون إما للعمل ببيانات غير مكتوبة (مما يفقد فوائد TypeScript
) أو يقضون وقتًا طويلًا في إنشاء وصيانة أنواع يمكن أن تصبح قديمة بسهولة مع تطور واجهة برمجة التطبيقات.
فوائد تبني graphql-codegen
متعددة:
- سلامة الأنواع الشاملة (End-to-End Type Safety): من خلال توليد الأنواع مباشرة من مخططك، يضمن
graphql-codegen
أن كود الواجهة الأمامية الخاص بك متزامن دائمًا مع نموذج بيانات الواجهة الخلفية الخاصة بك. هذا يعني أنك تلتقط الأخطاء المتعلقة بالأنواع في وقت الترجمة (compile time)، قبل وقت طويل من وصولها إلى المستخدمين. - تجربة مطور محسنة (Improved Developer Experience): مع الأنواع التي تم إنشاؤها، تحصل على ميزات مثل الإكمال التلقائي والاقتراحات الذكية في محرر الكود الخاص بك، مما يجعل التطوير أسرع وأكثر كفاءة. لا مزيد من تخمين شكل استجابات واجهة برمجة التطبيقات الخاصة بك!
- تقليل الكود المتكرر (Boilerplate): لا يمكن لـ
graphql-codegen
توليد الأنواع فحسب، بل يمكنه أيضًا توليد خطافات جاهزة للاستخدام لعملاءGraphQL
المشهورين مثلApollo Client
وReact Query
. هذا يلغي الحاجة إلى كتابة منطق جلب البيانات المتكرر. - قابلية صيانة محسنة (Enhanced Maintainability): عندما يتغير مخطط واجهة برمجة التطبيقات الخاص بك، فإن أمرًا بسيطًا هو كل ما يتطلبه الأمر لإعادة توليد أنواعك. هذا يجعل قاعدة الكود الخاصة بك أسهل بكثير في الصيانة وإعادة الهيكلة بمرور الوقت.
يتوافق هذا النهج تمامًا مع فلسفة التطوير "schema-first
" (المخطط أولاً)، حيث يعمل مخطط GraphQL
كمصدر وحيد للحقيقة لواجهة برمجة التطبيقات الخاصة بك.
البدء: إعدادك الأول لـ graphql-codegen
دعنا نتعمق في الجوانب العملية لاستخدام graphql-codegen
. لهذا البرنامج التعليمي، سنفترض أن لديك فهمًا أساسيًا لـ GraphQL
وTypeScript
، وأن لديك Node.js
ومدير حزم (مثل npm
أو yarn
) مثبتين.
هدفنا هو إعداد graphql-codegen
لتطبيق React
بسيط يعرض قائمة بمنشورات المدونة.
الخطوة 1: تهيئة المشروع والتبعيات
أولاً، دعنا ننشئ مشروع React
جديدًا باستخدام TypeScript
ونثبت التبعيات اللازمة.Bash
npx create-react-app my-blog --template typescript
cd my-blog
npm install @apollo/client graphql
npm install -D @graphql-codegen/cli @graphql-codegen/client-preset typescript
فيما يلي تفصيل للتبعيات التي قمنا بتثبيتها:
@apollo/client
: عميلGraphQL
شائع لـReact
.graphql
: تبعية نظير (peer dependency
) مطلوبة من قبل كل منApollo Client
وgraphql-codegen
.@graphql-codegen/cli
: واجهة سطر الأوامر لـgraphql-codegen
.@graphql-codegen/client-preset
: إعداد مسبق حديث ومبسط يبسط التكوين لتطبيقات جانب العميل.typescript
: مترجمTypeScript
.
الخطوة 2: معالج تهيئة graphql-codegen
أسهل طريقة للبدء مع graphql-codegen
هي باستخدام معالج التهيئة الخاص به. قم بتشغيل الأمر التالي في الدليل الجذر لمشروعك:Bash
npx graphql-codegen init
سيطلب منك المعالج سلسلة من الأسئلة للمساعدة في تكوين مشروعك. فيما يلي مجموعة نموذجية من الإجابات لسيناريو تطبيق المدونة الخاص بنا:
- ما نوع التطبيق الذي تبنيه؟
Application built with React
- أين مخططك؟ قدم عنوان
URL
لنقطة نهاية واجهة برمجة تطبيقاتGraphQL
الخاصة بك. لهذا البرنامج التعليمي، يمكننا استخدام واجهة برمجة تطبيقات مثال متاحة للجمهور:https://swapi-graphql.netlify.app/.netlify/functions/index
- أين عملياتك وأجزائك (fragments)؟
src/**/*.tsx
(يخبر هذاgraphql-codegen
بالبحث عن استعلاماتGraphQL
في جميع ملفات.tsx
داخل دليلsrc
). - اختر الإضافات (plugins): سيقترح المعالج مجموعة من الإضافات. الإعدادات الافتراضية، بما في ذلك
typescript
،typescript-operations
، وtypescript-react-apollo
، هي نقطة بداية رائعة. - أين تكتب المخرجات؟
src/gql/
(سيؤدي هذا إلى إنشاء دليل جديدsrc/gql
لتخزين الملفات التي تم إنشاؤها). - هل تريد توليد ملف فحص (introspection file)؟
Yes
(يمكن أن يكون هذا مفيدًا للتطوير المحلي وإضافات بيئة التطوير المتكاملةIDE
). - كيف تسمي ملف التكوين؟
codegen.ts
- ما هو السكريبت في
package.json
الذي يجب أن يقوم بتشغيلcodegen
؟codegen
بعد الإجابة على هذه الأسئلة، سيقوم المعالج بإنشاء ملف codegen.ts
في الدليل الجذر لمشروعك وإضافة سكريبت codegen
إلى ملف package.json
الخاص بك.
الخطوة 3: فهم ملف التكوين (codegen.ts
)
ملف codegen.ts
هو قلب إعداد graphql-codegen
الخاص بك. دعنا نلقي نظرة على نسخة مبسطة مما يولده المعالج:TypeScript
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
overwrite: true,
schema: "https://swapi-graphql.netlify.app/.netlify/functions/index",
documents: "src/**/*.tsx",
generates: {
"src/gql/": {
preset: "client",
plugins: []
}
}
};
export default config;
دعنا نفصل خيارات التكوين الرئيسية:
overwrite: true
: يضمن هذا أن الملفات التي تم إنشاؤها مسبقًا يتم الكتابة فوقها في كل مرة تقوم فيها بتشغيل الأمر.schema
: يشير هذا إلى مصدر مخططGraphQL
الخاص بك. يمكن أن يكون عنوانURL
لنقطة نهاية حية، أو ملف.graphql
أو.json
محلي.documents
: هذا هو نمطglob
يخبرgraphql-codegen
أين يجد عملياتGraphQL
الخاصة بك (الاستعلاماتqueries
، الطفراتmutations
، والأجزاءfragments
).generates
: هذا هو الجزء الأكثر أهمية في التكوين. إنه كائن يمثل فيه كل مفتاح ملف أو دليل إخراج، وتحدد القيمة ما سيتم إنشاؤه.preset: "client"
:client-preset
هو طريقة قوية وموصى بها لتكوينgraphql-codegen
لتطبيقات الواجهة الأمامية. يجمع بين العديد من الإضافات ويوفر تجربة مبسطة. يقوم بتوليد دالةgraphql
ستستخدمها لكتابة استعلاماتك.
الخطوة 4: كتابة أول استعلام GraphQL
لك
الآن بعد أن تم تكوين graphql-codegen
، دعنا نكتب استعلام GraphQL
لجلب منشورات المدونة الخاصة بنا. أنشئ ملفًا جديدًا src/components/Posts.tsx
:TypeScript
import { gql } from '../gql/gql';
import { useQuery } from '@apollo/client';
const GET_POSTS = gql(`
query GetPosts {
allFilms {
films {
id
title
director
releaseDate
}
}
}
`);
const Posts = () => {
const { loading, error, data } = useQuery(GET_POSTS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return (
<div>
{data?.allFilms?.films?.map((film) => (
<div key={film?.id}>
<h2>{film?.title}</h2>
<p>Director: {film?.director}</p>
<p>Release Date: {film?.releaseDate}</p>
</div>
))}
</div>
);
};
export default Posts;
لاحظ أننا نستورد دالة gql
من الملف الذي تم إنشاؤه src/gql/gql.ts
. هذه هي دالة gql
التي يوفرها client-preset
وهي التي تمكن سحر استنتاج الأنواع (type inference
).
الخطوة 5: تشغيل graphql-codegen
ورؤية السحر
الآن، قم بتشغيل سكريبت codegen
من ملف package.json
الخاص بك:Bash
npm run codegen
سيقوم هذا الأمر بفحص المخطط، والعثور على استعلام GET_POSTS
الخاص بك، وتوليد أنواع TypeScript
المقابلة في دليل src/gql
.
إذا قمت الآن بفحص الكائن data
الذي يعيده خطاف useQuery
في Posts.tsx
، فسترى أنه مكتوب بالكامل (fully typed
)! ستوفر بيئة التطوير المتكاملة (IDE
) الخاصة بك إكمالًا تلقائيًا لـ data.allFilms.films
، وسيُحذرك TypeScript
إذا حاولت الوصول إلى حقل غير موجود في الاستعلام.
تعمق أكبر: مثال عملي لواجهة أمامية لمدونة
دعنا نوسع مثالنا لترسيخ فهمك. تخيل أن مدونتنا لديها مخطط أكثر تقليدية:GraphQL
type Author {
id: ID!
name: String!
}
type Post {
id: ID!
title: String!
content: String!
author: Author!
}
type Query {
posts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createPost(title: String!, content: String!, authorId: ID!): Post!
}
لنفترض أن هذا المخطط يعمل على http://localhost:4000/graphql
. سنقوم بتحديث ملف codegen.ts
الخاص بنا وفقًا لذلك:TypeScript
// codegen.ts
// ...
schema: "http://localhost:4000/graphql",
// ...
الآن، دعنا ننشئ مكونًا لعرض منشور واحد وآخر لإنشاء منشور جديد.
جلب منشور واحدTypeScript
// src/components/Post.tsx
import { gql } from '../gql/gql';
import { useQuery } from '@apollo/client';
import { useParams } from 'react-router-dom';
const GET_POST = gql(`
query GetPost($id: ID!) {
post(id: $id) {
id
title
content
author {
id
name
}
}
}
`);
const Post = () => {
const { id } = useParams<{ id: string }>();
const { loading, error, data } = useQuery(GET_POST, {
variables: { id },
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return (
<div>
<h2>{data?.post?.title}</h2>
<p>By {data?.post?.author?.name}</p>
<p>{data?.post?.content}</p>
</div>
);
};
export default Post;
بعد تشغيل npm run codegen
، سيتم توليد أنواع GetPostQuery
ومتغيراتها، مما يوفر سلامة الأنواع لخطاف useQuery
ونتيجته.
إنشاء منشور جديدTypeScript
// src/components/CreatePost.tsx
import { gql } from '../gql/gql';
import { useMutation } from '@apollo/client';
import { useState } from 'react';
const CREATE_POST = gql(`
mutation CreatePost($title: String!, $content: String!, $authorId: ID!) {
createPost(title: $title, content: $content, authorId: $authorId) {
id
}
}
`);
const CreatePost = () => {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [createPost, { data, loading, error }] = useMutation(CREATE_POST);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
createPost({ variables: { title, content, authorId: '1' } }); // Assuming authorId '1' for simplicity
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Content"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<button type="submit" disabled={loading}>
{loading ? 'Creating...' : 'Create Post'}
</button>
{error && <p>Error creating post: {error.message}</p>}
{data && <p>Post created with ID: {data.createPost.id}</p>}
</form>
);
};
export default CreatePost;
هنا، يقوم graphql-codegen
بتوليد أنواع CreatePostMutation
ومتغيراتها. يضمن هذا أنه عند استدعاء دالة createPost
، فإنك تمرر الأنواع الصحيحة لـ title
، content
، وauthorId
.
مفاهيم متقدمة وأفضل الممارسات
مع ازدياد راحتك في استخدام graphql-codegen
، ستصادف ميزات أكثر تقدمًا وأفضل الممارسات:
- الأجزاء (Fragments): يتمتع
graphql-codegen
بدعم ممتاز لأجزاءGraphQL
. يمكنك تعريف الأجزاء في ملفات.tsx
الخاصة بك وسيقومgraphql-codegen
بالتعامل مع الأنواع بشكل صحيح، مما يعزز تبعيات البيانات القابلة لإعادة الاستخدام والموجودة مع المكونات الخاصة بك. - استخدام ملفات
.graphql
: لفصل أفضل للمخاوف، يمكنك كتابة استعلاماتGraphQL
، الطفرات، والأجزاء الخاصة بك في ملفات.graphql
مخصصة. ما عليك سوى تحديث مصفوفةdocuments
في ملفcodegen.ts
الخاص بك لتضمين امتداد الملف هذا:documents: ["src/**/*.tsx", "src/**/*.graphql"]
. - القياسات المخصصة (Custom Scalars): إذا كان مخطط
GraphQL
الخاص بك يستخدم قياسات مخصصة (مثلDate
،JSON
)، يمكنك ربطها بأنواعTypeScript
المقابلة في ملفcodegen.ts
الخاص بك للحفاظ على سلامة الأنواع. - وضع المراقبة (Watch Mode): لتجربة تطوير سلسة، يمكنك تشغيل
graphql-codegen
في وضع المراقبة. سيؤدي هذا إلى إعادة توليد أنواعك تلقائيًا كلما قمت بحفظ ملف يحتوي على عمليةGraphQL
. ما عليك سوى إضافة علامة--watch
إلى سكريبتcodegen
في ملفpackage.json
:"codegen": "graphql-codegen --watch"
.
استكشاف الأخطاء الشائعة وإصلاحها
بينما يعتبر graphql-codegen
أداة قوية، قد تواجه بعض المشكلات الشائعة كمبتدئ:
"Unable to find schema"
: يعني هذا الخطأ عادةً أن مسارschema
في ملفcodegen.ts
الخاص بك غير صحيح أو أن خادمGraphQL
لا يعمل على العنوان المحدد.Plugin Configuration Errors
(أخطاء تكوين الإضافات): تأكد من تثبيت جميع الإضافات اللازمة وأن تكويناتها في ملفcodegen.ts
صحيحة.graphql
Peer Dependency Issues (مشكلات تبعية النظير لـgraphql
): تأكد من تثبيتgraphql
كـ تبعية مباشرة في مشروعك.
الخلاصة: احتضن المستقبل الآمن للأنواع
graphql-codegen
هو أكثر من مجرد أداة لتوليد الكود؛ إنه تحول نموذجي في كيفية بناء التطبيقات باستخدام GraphQL
. من خلال تبني الأتمتة والاستفادة من قوة مخططك، يمكنك إنشاء قواعد كود أكثر قوة وقابلية للصيانة وممتعة للعمل معها.
لقد قدم لك هذا البرنامج التعليمي أساسًا قويًا للبدء مع graphql-codegen
. لقد غطينا المفاهيم الأساسية، وتطرقنا إلى مثال عملي، وتناولنا أفضل الممارسات. الرحلة لا تنتهي هنا. نظام graphql-codegen
البيئي واسع، مع مجموعة غنية من الإضافات التي تلبي مختلف الأطر وحالات الاستخدام. أشجعك على استكشاف التوثيق الرسمي، وتجربة إضافات مختلفة، واكتشاف كيف يمكن لـ graphql-codegen
أن يحدث ثورة في سير عمل تطوير GraphQL
الخاص بك. برمجة سعيدة!
هل تريد منصة متكاملة وشاملة لفريق المطورين الخاص بك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر أقل بكثير!