Zig adalah bahasa pemrograman modern yang dirancang agar kuat, optimal, dan mudah dipelihara. Dengan fokus pada kinerja, kontrol eksplisit atas alokasi memori, dan fitur waktu kompilasi, Zig adalah pilihan yang sangat baik untuk membangun aplikasi berperforma tinggi, termasuk REST API yang perlu menangani beban signifikan dengan penggunaan sumber daya minimal.
Dalam tutorial ini, kita akan menjelajahi cara memanfaatkan Zig untuk membuat REST API yang tidak hanya fungsional tetapi juga "sangat cepat". Kita akan membahas cara menyiapkan proyek, mengimplementasikan fungsionalitas API inti, dan mengoptimalkannya untuk kinerja puncak. Pada akhirnya, Anda akan memiliki dasar yang kuat untuk membangun layanan web berperforma tinggi menggunakan Zig.

Apidog menggabungkan dokumentasi, desain, pengujian, dan debugging API ke dalam satu alur kerja yang mulus, menjadikannya sempurna untuk menguji API Zig berperforma tinggi kita.

Menyiapkan Lingkungan Pengembangan Zig Anda
Sebelum kita mulai membuat kode, mari pastikan Anda memiliki semua yang dibutuhkan untuk mengembangkan dengan Zig:
Instal Zig: Unduh versi terbaru dari ziglang.org atau gunakan pengelola paket:
# On macOS with Homebrew
brew install zig
# On Linux with apt
sudo apt install zig
Verifikasi instalasi:
zig version
Struktur Proyek
Mari buat struktur proyek yang bersih:
mkdir zig-rest-api
cd zig-rest-api
zig init-exe
Ini membuat proyek Zig dasar dengan file src/main.zig
. Kita akan memperluas struktur ini:
zig-rest-api/
├── src/
│ ├── main.zig
│ ├── server.zig
│ ├── router.zig
│ ├── handlers/
│ │ ├── users.zig
│ │ └── products.zig
│ └── models/
│ ├── user.zig
│ └── product.zig
├── build.zig
└── README.md
Implementasi Server HTTP
Pertama, mari buat server HTTP dasar. Pustaka standar Zig menyediakan primitif jaringan, tetapi tidak menyertakan server HTTP lengkap. Kita akan menggunakan paket zhttp
yang sangat baik.
Tambahkan dependensi ini ke build.zig
Anda:
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);
}
Sekarang mari kita implementasikan server kita di 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 mendengarkan di port {d}", .{self.server.port});
try self.server.start();
}
};
Menambahkan Router dan Penanganan Permintaan
Di src/router.zig
, mari buat router sederhana:
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("Tidak Ditemukan");
return;
};
try handler(req, res);
}
};
Penanganan JSON
Mari buat fungsi utilitas untuk bekerja dengan 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, .{});
}
Membangun Handler API
Sekarang mari buat handler untuk sumber daya pengguna di 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("Parameter ID Hilang");
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("Pengguna tidak ditemukan");
}
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);
}
Menyatukan Semuanya
Sekarang, mari perbarui src/main.zig
kita untuk mengintegrasikan semuanya:
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);
}
Optimasi Kinerja
Zig menyediakan beberapa fitur yang membuat API kita sangat cepat:
- Abstraksi tanpa biaya: Pendekatan Zig memastikan bahwa abstraksi tidak menambah overhead.
- Metaprogramming waktu kompilasi: Kita dapat melakukan pekerjaan pada waktu kompilasi alih-alih waktu proses.
- Manajemen memori manual: Dengan mengontrol alokasi, kita menghindari jeda pengumpulan sampah.
- Alokator arena: Sempurna untuk memori lingkup permintaan yang dapat dengan cepat dibebaskan sekaligus.
- Fungsi inline: Jalur kritis dapat di-inline untuk menghilangkan overhead panggilan fungsi.
Mari optimalkan router kita:
// Add inline attribute to critical functions
pub inline fn handleRequest(self: *Router, req: zhttp.Request, res: *zhttp.Response) !void {
// Existing code...
}
Dan optimalkan handler permintaan kita dengan 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
}
}
};
Kesimpulan
Dalam tutorial ini, kita telah menjelajahi cara memanfaatkan fitur berfokus pada kinerja Zig untuk membangun REST API berperforma tinggi. Dengan memanfaatkan manajemen memori manual Zig, fitur waktu kompilasi, dan abstraksi tanpa biaya, kita telah membuat server API yang kuat dan sangat cepat.
Kita membahas:
- Menyiapkan lingkungan pengembangan Zig
- Membangun server HTTP dasar
- Membuat router untuk menangani titik akhir API
- Mengimplementasikan serialisasi dan deserialisasi JSON
- Menambahkan handler titik akhir API
- Mengoptimalkan untuk kinerja
Dasar ini memberi Anda alat untuk membangun API siap produksi di Zig yang mengungguli yang ditulis dalam banyak bahasa lain. Saat Anda terus mengembangkan dengan Zig, jelajahi fitur yang lebih canggih seperti comptime, penanganan kesalahan, dan kompilasi silang untuk lebih meningkatkan aplikasi Anda.