مقدمة: لماذا Go هو الخيار الأمثل لـ REST API
Go (أو Golang) هي لغة برمجة مترجمة من Google، تجمع بين أداء C++ وبساطة Python. إذا كنت ترغب في إنشاء REST API سريع وموثوق وسهل الصيانة، فإن Go هو أحد أفضل الخيارات. في هذه المقالة، سنكتب خلال 30 دقيقة REST API كامل بلغة Go، قادر على إنشاء وقراءة وتحديث وحذف السجلات (CRUD). لا أطر عمل معقدة - فقط المكتبة القياسية net/http وبعض الكود.
في نهاية المقالة، ستحصل على خادم HTTP جاهز يستجيب للطلبات بتنسيق JSON ويعمل دون اعتماديات خارجية. هيا بنا!
الخطوة 1: إعداد المشروع وهيكله
أولاً، تأكد من تثبيت Go (الإصدار 1.16+). أنشئ دليلاً جديداً للمشروع وقم بتهيئة الوحدة النمطية:
mkdir go-rest-apicd go-rest-apigo mod init go-rest-apiسيكون هيكل المشروع بسيطاً - ملف واحد main.go. سنضع كل الكود فيه، حتى لا نعقد المثال. في المشاريع الحقيقية، ستقسم الكود إلى حزم، لكن للتعلم هذا هو الخيار المثالي.
الخطوة 2: تعريف هيكل البيانات والتخزين العام
سيدير API الخاص بنا قائمة بالكتب. لننشئ هيكل Book وشريحة بسيطة لتخزين البيانات في الذاكرة (بدلاً من قاعدة البيانات).
package main
import ( "encoding/json" "log" "net/http" "strconv" "strings")
type Book struct { ID int `json:"id"` Title string `json:"title"` Author string `json:"author"` Year int `json:"year"`}
var books []Bookvar nextID int = 1هنا books هي "طبقة البيانات" المؤقتة لدينا، و nextID هو عداد لتعيين المعرفات تلقائياً للكتب الجديدة.
الخطوة 3: تنفيذ معالجات طلبات HTTP (CRUD)
الآن الجزء الممتع - سنكتب دوال المعالجة لكل طريقة HTTP. سنستخدم الموجه القياسي http.NewServeMux.
3.1. الحصول على جميع الكتب (GET /books)
func getBooks(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(books)}3.2. إنشاء كتاب جديد (POST /books)
func createBook(w http.ResponseWriter, r *http.Request) { var newBook Book if err := json.NewDecoder(r.Body).Decode(&newBook); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return } newBook.ID = nextID nextID++ books = append(books, newBook) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(newBook)}3.3. الحصول على كتاب واحد بواسطة المعرف (GET /books/{id})
في ServeMux القياسي لا يوجد دعم للمسارات الديناميكية، لذلك سنستخرج المعرف من URL يدوياً.
func getBook(w http.ResponseWriter, r *http.Request) { idStr := strings.TrimPrefix(r.URL.Path, "/books/") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid ID", http.StatusBadRequest) return } for _, book := range books { if book.ID == id { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(book) return } } http.Error(w, "Book not found", http.StatusNotFound)}3.4. تحديث كتاب (PUT /books/{id})
func updateBook(w http.ResponseWriter, r *http.Request) { idStr := strings.TrimPrefix(r.URL.Path, "/books/") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid ID", http.StatusBadRequest) return } var updatedBook Book if err := json.NewDecoder(r.Body).Decode(&updatedBook); err != nil { h