Wie man Zig verwendet, um eine REST-API zu erstellen

In diesem Tutorial: Zig für eine rasante REST-API.

Leo Schulz

Leo Schulz

9 September 2025

Wie man Zig verwendet, um eine REST-API zu erstellen

enterprise.banner.title

enterprise.banner.feature1

enterprise.banner.feature2

enterprise.banner.feature3

enterprise.banner.ctaB

Zig ist eine moderne Programmiersprache, die robust, optimal und wartbar sein soll. Mit ihrem Fokus auf Leistung, expliziter Kontrolle über die Speicherzuweisung und Compile-Time-Funktionen ist Zig eine ausgezeichnete Wahl für die Entwicklung von Hochleistungsanwendungen, einschließlich REST-APIs, die erhebliche Lasten mit minimalem Ressourcenverbrauch bewältigen müssen.

In diesem Tutorial werden wir untersuchen, wie man Zig einsetzt, um eine REST-API zu erstellen, die nicht nur funktional, sondern auch "rasend schnell" ist. Wir werden die Einrichtung eines Projekts, die Implementierung der Kernfunktionalität der API und deren Optimierung für maximale Leistung durchgehen. Am Ende haben Sie eine solide Grundlage für die Erstellung von Hochleistungs-Webdiensten mit Zig.

💡
Während viele Entwickler mit Postman als API-Testtool vertraut sind, möchte ich Ihnen Apidog vorstellen, eine leistungsstarke Alternative, die eine komplette API-Entwicklungsplattform bietet.

Apidog kombiniert API-Dokumentation, Design, Tests und Debugging in einem nahtlosen Workflow und ist damit perfekt für das Testen unserer Hochleistungs-Zig-API.

button

Einrichten Ihrer Zig-Entwicklungsumgebung

Bevor wir mit dem Codieren beginnen, stellen wir sicher, dass Sie alles haben, was Sie für die Entwicklung mit Zig benötigen:

Installieren Sie Zig: Laden Sie die neueste Version von ziglang.org herunter oder verwenden Sie einen Paketmanager:

# On macOS with Homebrew
brew install zig

# On Linux with apt
sudo apt install zig

Installation überprüfen:

zig version

Projektstruktur

Lassen Sie uns eine saubere Projektstruktur erstellen:

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

Dadurch wird ein einfaches Zig-Projekt mit einer src/main.zig-Datei erstellt. Wir werden diese Struktur erweitern:

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-Server-Implementierung

Erstellen wir zuerst einen einfachen HTTP-Server. Die Standardbibliothek von Zig bietet Netzwerkprimitive, enthält aber keinen vollständigen HTTP-Server. Wir verwenden das ausgezeichnete zhttp-Paket.

Fügen Sie diese Abhängigkeit zu Ihrer build.zig hinzu:

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);
}

Implementieren wir nun unseren Server in 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("Server listening on port {d}", .{self.server.port});
        try self.server.start();
    }
};

Hinzufügen von Router und Anforderungsbehandlung

Erstellen wir in src/router.zig einen einfachen Router:

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("Not Found");
            return;
        };

        try handler(req, res);
    }
};

JSON-Verarbeitung

Erstellen wir Hilfsfunktionen für die Arbeit mit 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, .{});
}

Erstellen von API-Handlern

Erstellen wir nun einen Handler für Benutzerressourcen in 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,
};

// In-memory store for demonstration
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 {
    // Extract ID from URL path parameters
    const id_str = req.params.get("id") orelse {
        res.status = .bad_request;
        try res.send("Missing ID parameter");
        return;
    };
    
    const id = try std.fmt.parseInt(usize, id_str, 10);
    
    // Find user with matching ID
    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("User not found");
}

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);
    
    // Generate a new 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);
}

Alles zusammenfügen

Aktualisieren wir nun unsere src/main.zig, um alles zu integrieren:

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 {
    // Create an arena allocator
    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();

    // Register routes
    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();

    // Set up the request handler
    server.server.setRequestHandler(handler);

    // Start the server
    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);
}

Leistungsoptimierungen

Zig bietet mehrere Funktionen, die unsere API rasend schnell machen:

  1. Zero-Cost-Abstraktionen: Der Ansatz von Zig stellt sicher, dass Abstraktionen keinen Overhead verursachen.
  2. Compile-Time-Metaprogrammierung: Wir können die Arbeit zur Compile-Zeit statt zur Laufzeit ausführen.
  3. Manuelle Speicherverwaltung: Durch die Steuerung der Zuweisungen vermeiden wir Pausen durch die Garbage Collection.
  4. Arena-Allokatoren: Perfekt für anforderungsbezogenen Speicher, der schnell auf einmal freigegeben werden kann.
  5. Inline-Funktionen: Kritische Pfade können inline gesetzt werden, um den Overhead durch Funktionsaufrufe zu eliminieren.

Optimieren wir unseren Router:

// Add inline attribute to critical functions
pub inline fn handleRequest(self: *Router, req: zhttp.Request, res: *zhttp.Response) !void {
    // Existing code...
}

Und optimieren Sie unseren Request-Handler mit einem Thread-Pool:

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 {
        // Process incoming requests
        while (true) {
            // Get request from queue and process
        }
    }
};

Fazit

In diesem Tutorial haben wir untersucht, wie man die auf Leistung ausgerichteten Funktionen von Zig nutzen kann, um eine Hochleistungs-REST-API zu erstellen. Durch die Nutzung der manuellen Speicherverwaltung, der Compile-Time-Funktionen und der Zero-Cost-Abstraktionen von Zig haben wir einen API-Server erstellt, der sowohl robust als auch rasend schnell ist.

Wir haben Folgendes behandelt:

Diese Grundlage gibt Ihnen die Werkzeuge an die Hand, um produktionsbereite APIs in Zig zu erstellen, die diejenigen in vielen anderen Sprachen übertreffen. Wenn Sie weiterhin mit Zig entwickeln, erkunden Sie erweiterte Funktionen wie Comptime, Fehlerbehandlung und Cross-Compilation, um Ihre Anwendungen weiter zu verbessern.

Explore more

Fathom-R1-14B: Fortschrittliches KI-Argumentationsmodell aus Indien

Fathom-R1-14B: Fortschrittliches KI-Argumentationsmodell aus Indien

Künstliche Intelligenz wächst rasant. FractalAIResearch/Fathom-R1-14B (14,8 Mrd. Parameter) glänzt in Mathe & Logik.

5 June 2025

Cursor 1.0 mit BugBot: KI-gestütztes Automatisierungstest-Tool ist da:

Cursor 1.0 mit BugBot: KI-gestütztes Automatisierungstest-Tool ist da:

Die Softwareentwicklung erlebt Innovationen durch KI. Cursor, ein KI-Editor, erreicht mit Version 1.0 einen Meilenstein.

5 June 2025

30+ öffentliche Web 3.0 APIs, die Sie jetzt nutzen können

30+ öffentliche Web 3.0 APIs, die Sie jetzt nutzen können

Der Aufstieg von Web 3.0: Dezentral, nutzerorientiert, transparent. APIs ermöglichen innovative dApps und Blockchain-Integration.

4 June 2025

Praktizieren Sie API Design-First in Apidog

Entdecken Sie eine einfachere Möglichkeit, APIs zu erstellen und zu nutzen