🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

كيفية إنشاء واجهة برمجة تطبيقات GraphQL باستخدام Node.js وApollo Server في عام 2026

⏱️2 min read  ·  431 words

الرسم البيانيQL يتيح للعملاء طلب البيانات التي يحتاجونها بالضبط في طلب واحد – بدون جلب زائد أو نقص في الجلب. مدمج معخادم أبولو وTypeScript، إنها طريقة قوية لإنشاء واجهات برمجة التطبيقات المرنة. يبني هذا الدليل واجهة برمجة تطبيقات GraphQL كاملة من البداية.

GraphQL مقابل REST — مقارنة سريعة

الجانب راحة الرسم البيانيQL
جلب البيانات نقاط نهاية متعددة نقطة نهاية واحدة، استعلامات دقيقة
الإفراط في الجلب مشترك تم الحذف
الإصدار إصدارات URL (الإصدار 1، الإصدار 2) تطور المخطط
الأفضل لـ CRUD بسيط، التخزين المؤقت بيانات معقدة ومتداخلة ويعتمد عليها العميل

إعداد المشروع

mkdir graphql-api && cd graphql-api
npm init -y
npm install @apollo/server graphql
npm install -D typescript tsx @types/node
npx tsc --init

تحديد المخطط

// src/schema.ts
export const typeDefs = `#graphql
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
    published: Boolean!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
    posts: [Post!]!
  }

  type Mutation {
    createUser(name: String!, email: String!): User!
    createPost(title: String!, content: String!, authorId: ID!): Post!
    publishPost(id: ID!): Post!
  }
`;

كتابة الحلول

// src/resolvers.ts
import { db } from './db';

export const resolvers = {
  Query: {
    users: () => db.users.findAll(),
    user: (_: any, { id }: { id: string }) => db.users.findById(id),
    posts: () => db.posts.findAll(),
  },

  Mutation: {
    createUser: (_: any, { name, email }: { name: string; email: string }) => {
      return db.users.create({ name, email });
    },
    createPost: (_: any, args: { title: string; content: string; authorId: string }) => {
      return db.posts.create({ ...args, published: false });
    },
    publishPost: (_: any, { id }: { id: string }) => {
      return db.posts.update(id, { published: true });
    },
  },

  // Field resolvers for relationships
  User: {
    posts: (parent: { id: string }) => db.posts.findByAuthor(parent.id),
  },
  Post: {
    author: (parent: { authorId: string }) => db.users.findById(parent.authorId),
  },
};

إنشاء الخادم

// src/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs } from './schema';
import { resolvers } from './resolvers';

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

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
  context: async ({ req }) => {
    // Extract auth token, attach user to context
    const token = req.headers.authorization?.replace('Bearer ', '');
    const user = token ? await verifyToken(token) : null;
    return { user };
  },
});

console.log("GraphQL ready at " + url);

المصادقة في المحللون

Mutation: {
  createPost: (_: any, args, context) => {
    // Require authentication
    if (!context.user) {
      throw new GraphQLError('Not authenticated', {
        extensions: { code: 'UNAUTHENTICATED' },
      });
    }
    return db.posts.create({ ...args, authorId: context.user.id });
  },
},

حل مشكلة N+1 مع DataLoader

يمكن لوحدات حل الحقول تشغيل استعلامات N+1 (استعلام واحد لكل أصل). يقوم DataLoader بتجميعها وتخزينها مؤقتًا:

npm install dataloader
import DataLoader from 'dataloader';

// Create a loader that batches user lookups
const userLoader = new DataLoader(async (ids: readonly string[]) => {
  const users = await db.users.findByIds(ids);
  // Return in the same order as requested ids
  return ids.map(id => users.find(u => u.id === id));
});

// Use in resolver — batches all author lookups into one query
Post: {
  author: (parent, _, context) => context.userLoader.load(parent.authorId),
},

استعلام مثال

# Client requests exactly what it needs
query {
  user(id: "1") {
    name
    posts {
      title
      published
    }
  }
}

# Response contains only requested fields:
# { "data": { "user": { "name": "Alice", "posts": [...] } } }

الأسئلة المتداولة

س: GraphQL أو REST لمشروعي؟
ج: GraphQL للبيانات المعقدة والمتداخلة والموجهة بواسطة العميل (تطبيقات الهاتف المحمول ولوحات المعلومات ذات الاحتياجات المتنوعة). REST لـ CRUD البسيط وعندما يكون التخزين المؤقت لـ HTTP أكثر أهمية. تستخدم العديد من الفرق كليهما.

س: كيف أتعامل مع تحميلات الملفات في GraphQL؟
ج: استخدم حزمة graphql-upload، أو الأفضل من ذلك، تحميل الملفات إلى وحدة تخزين الكائنات (S3) عبر نقطة نهاية REST منفصلة أو عنوان URL موقّع مسبقًا، وقم بتمرير عنوان URL في طفرة GraphQL الخاصة بك.

س: هل تخزين GraphQL أصعب من تخزين REST؟
ج: نعم على مستوى HTTP (نقطة نهاية واحدة، طلبات POST). استخدم ذاكرة التخزين المؤقت الخاصة بـ Apollo Client على العميل والاستعلامات المستمرة أو التخزين المؤقت للاستجابة على الخادم للتعويض.

س: كيف يمكنني منع الاستعلامات باهظة الثمن/الضارة؟
ج: قم بإضافة تحديد عمق الاستعلام، وتحليل التعقيد، وتحديد المعدل. تعمل حزم مثل graphql-deep-limit وgraphql-query-complexity على الحماية من الاستعلامات المسيئة المتداخلة بشدة.

س: هل يجب أن أستخدم الكود أولاً أم المخطط أولاً؟
ج: المخطط أولاً (سلاسل SDL، الموضحة هنا) واضح ولا يلتزم باللغة. يوفر Code-first (Nexus، TypeGraphQL) تكاملًا أفضل لـ TypeScript. كلاهما يعمل؛ اختر بناءً على تفضيلات الفريق.

الخلاصة

يوفر GraphQL مع Apollo Server للعملاء تحكمًا دقيقًا في البيانات التي يجلبونها، مما يؤدي إلى التخلص من الجلب الزائد أو الناقص. يغطي الإعداد هنا – المخطط المكتوب، والمحللات ذات العلاقات، والمصادقة المستندة إلى السياق، وDataLoader لمشكلة N+1 – أساسيات واجهة برمجة تطبيقات GraphQL للإنتاج. قم بإضافة تحديد العمق وتحليل التعقيد قبل بدء البث المباشر، وقم بالاقتران مع Apollo Client للحصول على ذاكرة تخزين مؤقت طبيعية على الواجهة الأمامية.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা