API بلغة Go: REST وGraphQL وgRPC — أيهما تختار في 2025

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

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

ابدأ الدورة

لماذا Go هي اللغة المثالية لـ API؟

\n\n

عند الحديث عن إنشاء APIs عالية التحميل، تحتل Go (Golang) بثقة مكانًا في القمة. طبيعتها المترجمة، والدعم المدمج للتوازي عبر goroutines، وبناء الجملة البسيط، تسمح بكتابة خدمات مصغرة سريعة وموثوقة. ولكن أي بروتوكول تختار لمشروعك القادم: REST الكلاسيكي، GraphQL المرن، أم gRPC فائق السرعة؟

\n\n

في هذه المقالة، لن نقتصر على سرد الاختلافات — بل سنكتب كودًا حقيقيًا بلغة Go لكل نهج، ونحلل اختبارات الأداء، ونقدم معايير واضحة للاختيار. هيا بنا!

\n\n

REST API بلغة Go: الكلاسيكية المثبتة

\n\n

يظل REST (Representational State Transfer) المعيار الفعلي لـ APIs العامة. تقدم Go مكتبة قياسية ممتازة net/http، بالإضافة إلى أطر عمل شائعة مثل gin و echo.

\n\n

مثال لخادم REST بلغة Go النقية

\n\n

لنبدأ بخدمة CRUD بسيطة لإدارة المهام. إليك كيف يبدو الحد الأدنى من REST API بلغة Go بدون تبعيات خارجية:

\n\n
package main\n\nimport (\n    "encoding/json"\n    "log"\n    "net/http"\n    "sync"\n)\n\ntype Task struct {\n    ID   int    `json:"id"`\n    Name string `json:"name"`\n}\n\nvar (\n    tasks   = []Task{{ID: 1, Name: "تعلم Go"}}\n    mu      sync.Mutex\n    nextID  = 2\n)\n\nfunc getTasks(w http.ResponseWriter, r *http.Request) {\n    mu.Lock()\n    defer mu.Unlock()\n    w.Header().Set("Content-Type", "application/json")\n    json.NewEncoder(w).Encode(tasks)\n}\n\nfunc createTask(w http.ResponseWriter, r *http.Request) {\n    var task Task\n    if err := json.NewDecoder(r.Body).Decode(&task); err != nil {\n        http.Error(w, err.Error(), http.StatusBadRequest)\n        return\n    }\n    mu.Lock()\n    task.ID = nextID\n    nextID++\n    tasks = append(tasks, task)\n    mu.Unlock()\n    w.WriteHeader(http.StatusCreated)\n    json.NewEncoder(w).Encode(task)\n}\n\nfunc main() {\n    http.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) {\n        switch r.Method {\n        case http.MethodGet:\n            getTasks(w, r)\n        case http.MethodPost:\n            createTask(w, r)\n        default:\n            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)\n        }\n    })\n    log.Println("REST API قيد التشغيل على :8080")\n    log.Fatal(http.ListenAndServe(":8080", nil))\n}\n
\n\n

مزايا REST على Go:

\n
    \n
  • البساطة والقابلية للتنبؤ — كل نقطة نهاية تعيد بنية بيانات ثابتة
  • \n
  • قابلية تخزين مؤقت ممتازة عبر رؤوس HTTP
  • \n
  • دعم واسع للأدوات (Swagger, Postman)
  • \n
\n\n

العيوب: الإفراط في جلب البيانات (Over-fetching) ونقص الجلب (Under-fetching). إذا كان العميل يحتاج فقط إلى اسم المهمة، فسيظل يحصل على JSON كامل.

\n\n

GraphQL على Go: مرونة الاستعلامات

\n\n

يحل GraphQL مشكلة تكرار البيانات. يقرر العميل بنفسه الحقول التي يحتاجها. يقدم مجتمع Go مكتبة قوية gqlgen، والتي تولد الكود من المخطط.

\n\n

مثال لخادم GraphQL مع gqlgen

\n\n

أولاً، نحدد المخطط schema.graphql:

\n\n
type Task {\n    id: ID!\n    name: String!\n}\n\ntype Query {\n    tasks: [Task!]!\n}\n\ntype Mutation {\n    createTask(name: String!): Task!\n}\n
\n\n

الآن، نشغل التوليد ونكتب المحللات (resolvers) بلغة Go:

\n\n
package main\n\nimport (\n    "log"\n    "net/http"\n    "github.com/99designs/gqlgen/graphql/handler"\n    "github.com/99designs/gqlgen/graphql/playground"\n)\n\n// نفترض أن الكود المُولَّد موجود بالفعل\ntype Resolver struct {\n    tasks []Task\n    mu    sync.Mutex\n}\n\nfunc (r *Resolver) Tasks(ctx context.Context) ([]Task, error) {\n    r.mu.Lock()\n    defer r.mu.Unlock()\n    return r.tasks, nil\n}\n\nfunc (r *Resolver) CreateTask(ctx context.Context, name string) (*Task, error) {\n    r.mu.Lock()\n    defer r.mu.Unlock()\n                

المدونات

توصيات الكتب