Apidog

منصة تطوير API تعاونية متكاملة

تصميم API

توثيق API

تصحيح أخطاء API

محاكاة API

اختبار API الآلي

كيفية استخدام Zig لبناء واجهة برمجة التطبيقات REST

@apidog

@apidog

Updated on أبريل 2, 2025

زيغ هي لغة برمجة حديثة مصممة لتكون قوية، مثالية، وسهلة الصيانة. مع تركيزها على الأداء، والتحكم الصريح في تخصيص الذاكرة، والميزات في وقت الترجمة، تُعتبر زيغ خيارًا ممتازًا لبناء تطبيقات عالية الأداء، بما في ذلك واجهات برمجة التطبيقات REST التي تحتاج إلى التعامل مع أحمال كبيرة باستخدام الحد الأدنى من استخدام الموارد.

في هذا الدليل، سنستكشف كيفية استغلال زيغ لإنشاء واجهة برمجة تطبيقات REST ليست وظيفية فحسب، بل "سريعة للغاية". سنتناول إعداد مشروع، وتنفيذ الوظائف الأساسية لواجهة برمجة التطبيقات، وتحسينها لأفضل أداء. بنهاية الدليل، سيكون لديك أساس قوي لبناء خدمات الويب عالية الأداء باستخدام زيغ.

💡
بينما يعرف العديد من المطورين أداة اختبار واجهات برمجة التطبيقات Postman، أود أن أقدم لكم Apidog، بديل قوي يوفر منصة كاملة لتطوير واجهات برمجة التطبيقات.

يجمع Apidog بين توثيق واجهات برمجة التطبيقات، والتصميم، والاختبار، وتصحيح الأخطاء في سير عمل سلس، مما يجعله مثاليًا لاختبار واجهة برمجة التطبيقات Zig عالية الأداء لدينا.

زر

إعداد بيئة تطوير زيغ الخاصة بك

قبل أن نبدأ في البرمجة، دعنا نتأكد من أن لديك كل ما تحتاجه لتطوير باستخدام زيغ:

تثبيت زيغ: قم بتنزيل أحدث إصدار من ziglang.org أو استخدم مدير الحزم:

# على نظام macOS باستخدام Homebrew
brew install zig

# على نظام Linux باستخدام apt
sudo apt install zig

تحقق من التثبيت:

zig version

هيكل المشروع

دعنا ننشئ هيكل مشروع نظيف:

mkdir zig-rest-api
cd zig-rest-api
zig init-exe

هذا ينشئ مشروع زيغ أساسي مع ملف src/main.zig. سنقوم بتوسيع هذا الهيكل:

zig-rest-api/
├── src/
│   ├── main.zig
│   ├── server.zig
│   ├── router.zig
│   ├── handlers/
│   │   ├── users.zig
│   │   └── products.zig
│   └── models/
│       ├── user.zig
│       └── product.zig
├── build.zig
└── README.md

تنفيذ خادم HTTP

أولاً، دعنا ننشئ خادم HTTP أساسي. توفر مكتبة زيغ القياسية أولويات الشبكة، لكنها لا تتضمن خادم HTTP كامل. سنستخدم حزمة zhttp الممتازة.

أضف هذه التبعية إلى build.zig:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const zhttp = b.dependency("zhttp", .{});
    
    const exe = b.addExecutable(.{
        .name = "zig-rest-api",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    exe.addModule("zhttp", zhttp.module("zhttp"));
    
    b.installArtifact(exe);
}

الآن دعنا ننفذ خادمنا في src/server.zig:

const std = @import("std");
const zhttp = @import("zhttp");
const Allocator = std.mem.Allocator;

pub const Server = struct {
    server: zhttp.Server,
    allocator: Allocator,

    pub fn init(allocator: Allocator, port: u16) !Server {
        return Server{
            .server = try zhttp.Server.init(allocator, .{
                .address = "0.0.0.0",
                .port = port,
            }),
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Server) void {
        self.server.deinit();
    }

    pub fn start(self: *Server) !void {
        std.log.info("الخادم يستمع على المنفذ {d}", .{self.server.port});
        try self.server.start();
    }
};

إضافة موجه ومعالجة الطلبات

في src/router.zig، دعنا ننشئ موجهًا بسيطًا:

const std = @import("std");
const zhttp = @import("zhttp");
const Allocator = std.mem.Allocator;

pub const Router = struct {
    routes: std.StringHashMap(HandlerFn),
    allocator: Allocator,

    pub const HandlerFn = fn(zhttp.Request, *zhttp.Response) anyerror!void;

    pub fn init(allocator: Allocator) Router {
        return Router{
            .routes = std.StringHashMap(HandlerFn).init(allocator),
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Router) void {
        self.routes.deinit();
    }

    pub fn addRoute(self: *Router, path: []const u8, handler: HandlerFn) !void {
        try self.routes.put(try self.allocator.dupe(u8, path), handler);
    }

    pub fn handleRequest(self: *Router, req: zhttp.Request, res: *zhttp.Response) !void {
        const handler = self.routes.get(req.path) orelse {
            res.status = .not_found;
            try res.send("غير موجود");
            return;
        };

        try handler(req, res);
    }
};

معالجة JSON

دعنا ننشئ وظائف مساعدة للعمل مع JSON:

// src/json_utils.zig
const std = @import("std");

pub fn parseJson(comptime T: type, json_str: []const u8, allocator: std.mem.Allocator) !T {
    var tokens = std.json.TokenStream.init(json_str);
    return try std.json.parse(T, &tokens, .{
        .allocator = allocator,
        .ignore_unknown_fields = true,
    });
}

pub fn stringify(value: anytype, allocator: std.mem.Allocator) ![]const u8 {
    return std.json.stringifyAlloc(allocator, value, .{});
}

بناء معالجات API

الآن دعنا ننشئ معالجًا لموارد المستخدمين في src/handlers/users.zig:

const std = @import("std");
const zhttp = @import("zhttp");
const json_utils = @import("../json_utils.zig");

const User = struct {
    id: usize,
    name: []const u8,
    email: []const u8,
};

// تخزين في الذاكرة لأغراض العرض
var users = std.ArrayList(User).init(std.heap.page_allocator);

pub fn getUsers(req: zhttp.Request, res: *zhttp.Response) !void {
    const json = try json_utils.stringify(users.items, req.allocator);
    res.headers.put("Content-Type", "application/json") catch unreachable;
    try res.send(json);
}

pub fn getUserById(req: zhttp.Request, res: *zhttp.Response) !void {
    // استخراج ID من معلمات مسار URL
    const id_str = req.params.get("id") orelse {
        res.status = .bad_request;
        try res.send("معلمة ID مفقودة");
        return;
    };
    
    const id = try std.fmt.parseInt(usize, id_str, 10);
    
    // العثور على مستخدم بالمعرف المطابق
    for (users.items) |user| {
        if (user.id == id) {
            const json = try json_utils.stringify(user, req.allocator);
            res.headers.put("Content-Type", "application/json") catch unreachable;
            try res.send(json);
            return;
        }
    }
    
    res.status = .not_found;
    try res.send("المستخدم غير موجود");
}

pub fn createUser(req: zhttp.Request, res: *zhttp.Response) !void {
    const body = try req.body();
    var user = try json_utils.parseJson(User, body, req.allocator);
    
    // إنشاء ID جديد
    user.id = users.items.len + 1;
    
    try users.append(user);
    
    res.status = .created;
    res.headers.put("Content-Type", "application/json") catch unreachable;
    const json = try json_utils.stringify(user, req.allocator);
    try res.send(json);
}

جمع كل شيء معًا

الآن، دعنا نحدث src/main.zig لدمج كل شيء:

const std = @import("std");
const Server = @import("server.zig").Server;
const Router = @import("router.zig").Router;
const users = @import("handlers/users.zig");

pub fn main() !void {
    // إنشاء مخصص للذاكرة
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    var router = Router.init(allocator);
    defer router.deinit();

    // تسجيل المسارات
    try router.addRoute("/api/users", users.getUsers);
    try router.addRoute("/api/users/{id}", users.getUserById);
    try router.addRoute("/api/users", users.createUser);

    var server = try Server.init(allocator, 8080);
    defer server.deinit();

    // إعداد معالج الطلبات
    server.server.setRequestHandler(handler);

    // بدء الخادم
    try server.start();
}

fn handler(req: zhttp.Request, res: *zhttp.Response) !void {
    std.log.info("{s} {s}", .{@tagName(req.method), req.path});
    try router.handleRequest(req, res);
}

تحسينات الأداء

توفر زيغ العديد من الميزات التي تجعل واجهة برمجة التطبيقات لدينا سريعة جدًا:

  1. التحويلات ذات التكلفة الصفرية: نهج زيغ يضمن أن التحويلات لا تضيف أي عبء.
  2. البرمجة الميتا في وقت الترجمة: يمكننا إجراء العمل في وقت الترجمة بدلاً من وقت التشغيل.
  3. إدارة الذاكرة يدوية: من خلال التحكم في التخصيصات، نتجنب فترات توقف جمع القمامة.
  4. مخصصات الحلبة: مثالية لذاكرة نطاق الطلب التي يمكن تحريرها بسرعة دفعة واحدة.
  5. الدوال المضمنة: يمكن أن تكون المسارات الحرجة مضمنة للقضاء على عبء استدعاء الدوال.

دعنا نحسن موجهنا:

// إضافة خاصية مضمنة للدوال الحرجة
pub inline fn handleRequest(self: *Router, req: zhttp.Request, res: *zhttp.Response) !void {
    // الكود الحالي...
}

ولتحسين معالج الطلبات لدينا باستخدام مجموعة مؤشرات:

const ThreadPool = struct {
    threads: []std.Thread,
    
    pub fn init(thread_count: usize) !ThreadPool {
        var threads = try std.heap.page_allocator.alloc(std.Thread, thread_count);
        
        for (threads) |*thread, i| {
            thread.* = try std.Thread.spawn(.{}, workerFn, .{i});
        }
        
        return ThreadPool{ .threads = threads };
    }
    
    fn workerFn(id: usize) void {
        // معالجة الطلبات الواردة
        while (true) {
            // الحصول على الطلب من قائمة الانتظار ومعالجته
        }
    }
};

الخاتمة

في هذا الدليل، استكشفنا كيفية استغلال ميزات زيغ التي تركز على الأداء لبناء واجهة برمجة تطبيقات REST عالية الأداء. من خلال الاستفادة من إدارة الذاكرة اليدوية في زيغ، وميزات وقت الترجمة، والتحويلات ذات التكلفة الصفرية، أنشأنا خادم واجهة برمجة تطبيقات يكون قويًا وسريعًا للغاية.

قمنا بتغطية:

  • إعداد بيئة تطوير زيغ
  • بناء خادم HTTP أساسي
  • إنشاء موجه للتعامل مع نقاط نهاية واجهة برمجة التطبيقات
  • تنفيذ تسلسل JSON وفك تسلسلها
  • إضافة معالجات نقاط نهاية واجهة برمجة التطبيقات
  • تحسين الأداء

يوفر لك هذا الأساس الأدوات اللازمة لبناء واجهات برمجة التطبيقات الجاهزة للإنتاج في زيغ التي تتفوق على تلك المكتوبة في العديد من اللغات الأخرى. بينما تستمر في تطوير متعلقات بزيغ، استكشف ميزات أكثر تقدمًا مثل comptime، والتعامل مع الأخطاء، والتجميع المتقاطع لتعزيز تطبيقاتك بشكل أكبر.