GraphQL مقابل REST في Kotlin: أيهما تختار للباك إند في 2025

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: هل مات REST، وليحيا GraphQL؟ (تنبيه: لا)

\n

إذا كنت تكتب بلغة Kotlin وتطور باك إند، فبالتأكيد سمعت عن الحرب المقدسة: GraphQL vs REST. في زاوية من الحلبة، لدينا REST القديم الجيد الذي يعرفه كل مبتدئ. وفي الزاوية الأخرى، لدينا GraphQL العصري الذي يعد بـ "أعطني ما لا أعرفه، ولكن باستعلام واحد فقط".

\n

أنا مطور باك إند ذو خبرة في Kotlin، وخلال العامين الماضيين، أعدت كتابة ثلاثة مشاريع من REST إلى GraphQL والعكس. تنبيه: كل أداة مناسبة لمهمتها. في هذه المقالة، لن نتعصب. بدلاً من ذلك، سنحلل بأمثلة كود حقيقية متى يفوز REST في Kotlin، ومتى يكون GraphQL هو منقذك.

\n

هيا بنا نلقي نظرة على مقارنة GraphQL و REST في Kotlin بدون حشو أو تسويق.

\n\n

1. باختصار عن الأساسيات: ما هو كل شيء

\n

قبل أن نخوض في الكود، دعونا نتفق على المصطلحات. REST API هو أسلوب معماري حيث كل مورد (مستخدم، طلب، منتج) له عنوان URL خاص به وطرق HTTP (GET, POST, PUT, DELETE). يستخدم مطورو Kotlin غالبًا Spring Boot أو Ktor لإنشاء خدمات REST.

\n

GraphQL هي لغة استعلامات حيث لديك نقطة نهاية واحدة (عادةً /graphql)، ويقرر العميل بنفسه الحقول التي يحتاجها. في Kotlin، المكتبات الشائعة هي KGraphQL (خفيفة الوزن) و Spring for GraphQL (لعشاق Spring).

\n

الفرق الرئيسي: REST يعني "الخادم يقرر ما يرسله". GraphQL يعني "العميل يطلب فقط ما يحتاجه".

\n\n

2. مثال على REST API في Kotlin (Ktor)

\n

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

\n

هذا ما يبدو عليه وحدة تحكم REST نموذجية على Ktor (إطار عمل خفيف لـ Kotlin):

\n\n
// REST endpoint: GET /api/users/{id}\nfun Application.userModule() {\n    routing {\n        get("/api/users/{id}") {\n            val userId = call.parameters["id"]?.toIntOrNull() ?: return@get call.respondText("Bad Request", status = HttpStatusCode.BadRequest)\n            val user = userService.getUserById(userId) ?: return@get call.respondText("Not Found", status = HttpStatusCode.NotFound)\n            call.respond(user)\n        }\n    }\n}\n\n// Data class للاستجابة\n@Serializable\ndata class UserResponse(\n    val id: Int,\n    val name: String,\n    val email: String,\n    val lastOrders: List<Order>\n)
\n\n

المشكلة: إذا كان العميل (تطبيق الجوال) يحتاج فقط إلى البريد الإلكتروني للمستخدم، فإن الخادم سيرسل كائن UserResponse الكامل مع الطلبات. هذا هو over-fetching — نقل بيانات زائد. على شبكات الجوال الضعيفة، هذا مؤلم.

\n

الحل الأول هو إنشاء عدة نقاط نهاية صغيرة: GET /api/users/{id}/email. ولكن هذا يزيد من عدد الطلبات (under-fetching).

\n\n

3. مثال على GraphQL في Kotlin (Spring for GraphQL)

\n

الآن نفس السيناريو، ولكن مع GraphQL. هذا هو المخطط (schema.graphqls):

\n\n
type Query {\n    user(id: ID!): User\n}\n\ntype User {\n    id: ID!\n    name: String\n    email: String\n    lastOrders: [Order]\n}\n\ntype Order {\n    id: ID!\n    total: Float\n    date: String\n}
\n\n

وهذا هو كود Kotlin باستخدام Spring for GraphQL (النهج القائم على التعليقات التوضيحية):

\n\n
@Controller\nclass UserController(private val userService: UserService) {\n\n    @QueryMapping\n    fun user(@Argument id: Int): User {\n        return userService.getUserById(id) ?: throw UserNotFoundException("User not found")\n    }\n}\n\n// Data class بدون حقول زائدة\ndata class User(val id: Int, val name: String, val email: String, val lastOrders: List<Order>)
\n\n

الآن يمكن للعميل طلب ما يحتاجه فقط:

\n\n
query {\n    user(id: 42) {\n        email\n    }\n}

المدونات

توصيات الكتب