Construindo uma API REST Completa com Zig
Neste artigo, construímos uma API REST funcional com Zig do zero: roteamento de URLs, serialização/desserialização JSON, middleware de logging e validação, tratamento de erros estruturado e exemplos de integração com banco de dados.
Estrutura do Projeto
minha-api/
├── build.zig
├── build.zig.zon
└── src/
├── main.zig
├── router.zig
├── handlers/
│ ├── usuarios.zig
│ └── health.zig
└── models/
└── usuario.zig
Modelo de Dados
// src/models/usuario.zig
const std = @import("std");
pub const Usuario = struct {
id: u64,
nome: []const u8,
email: []const u8,
ativo: bool,
pub fn toJson(self: Usuario, allocator: std.mem.Allocator) ![]const u8 {
var buf = std.ArrayList(u8).init(allocator);
const writer = buf.writer();
try writer.print(
\\{{"id":{d},"nome":"{s}","email":"{s}","ativo":{s}}}
, .{ self.id, self.nome, self.email, if (self.ativo) "true" else "false" });
return buf.toOwnedSlice();
}
};
Roteador
// src/router.zig
const std = @import("std");
const http = std.http;
pub const Handler = *const fn (*http.Server.Response, std.mem.Allocator) anyerror!void;
pub const Router = struct {
rotas: std.StringHashMap(Handler),
pub fn init(allocator: std.mem.Allocator) Router {
return .{ .rotas = std.StringHashMap(Handler).init(allocator) };
}
pub fn registrar(self: *Router, path: []const u8, handler: Handler) !void {
try self.rotas.put(path, handler);
}
pub fn despachar(self: *Router, response: *http.Server.Response, allocator: std.mem.Allocator) !void {
const path = response.request.target;
if (self.rotas.get(path)) |handler| {
try handler(response, allocator);
} else {
response.status = .not_found;
try response.do();
try response.writeAll("{\"erro\": \"rota não encontrada\"}");
try response.finish();
}
}
};
Handler de Usuários
// src/handlers/usuarios.zig
const std = @import("std");
const http = std.http;
const Usuario = @import("../models/usuario.zig").Usuario;
// Simulação de banco de dados em memória
var proximo_id: u64 = 1;
pub fn listar(response: *http.Server.Response, allocator: std.mem.Allocator) !void {
_ = allocator;
response.status = .ok;
try response.do();
try response.writeAll("[{\"id\":1,\"nome\":\"Ana\",\"email\":\"ana@exemplo.com\",\"ativo\":true}]");
try response.finish();
}
pub fn criar(response: *http.Server.Response, allocator: std.mem.Allocator) !void {
// Ler body da requisição
var buf: [4096]u8 = undefined;
const body_len = try response.readAll(&buf);
const body = buf[0..body_len];
// Parse JSON
const parsed = std.json.parseFromSlice(
struct { nome: []const u8, email: []const u8 },
allocator,
body,
.{},
) catch {
response.status = .bad_request;
try response.do();
try response.writeAll("{\"erro\": \"JSON inválido\"}");
try response.finish();
return;
};
defer parsed.deinit();
const usuario = Usuario{
.id = proximo_id,
.nome = parsed.value.nome,
.email = parsed.value.email,
.ativo = true,
};
proximo_id += 1;
const json = try usuario.toJson(allocator);
defer allocator.free(json);
response.status = .created;
try response.do();
try response.writeAll(json);
try response.finish();
}
Servidor Principal
// src/main.zig
const std = @import("std");
const Router = @import("router.zig").Router;
const usuarios = @import("handlers/usuarios.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var router = Router.init(allocator);
try router.registrar("/api/v1/usuarios", usuarios.listar);
try router.registrar("/health", healthCheck);
const endereco = std.net.Address.initIp4(.{ 0, 0, 0, 0 }, 8080);
var server = std.http.Server.init(allocator, .{});
try server.listen(endereco);
std.log.info("API rodando em http://localhost:8080", .{});
while (true) {
var response = try server.accept();
defer response.deinit();
try router.despachar(&response, allocator);
}
}
fn healthCheck(response: *std.http.Server.Response, _: std.mem.Allocator) !void {
response.status = .ok;
try response.do();
try response.writeAll("{\"status\":\"healthy\"}");
try response.finish();
}
Conclusão
Construir uma API REST em Zig exige mais código boilerplate do que em Go ou Node.js, mas o resultado é um servidor extremamente performático, com binário estático de poucos MB, startup instantâneo e uso mínimo de memória. Para APIs de alta performance, Zig é uma excelente escolha.
Conteúdo Relacionado
- Servidor HTTP em Zig — Tutorial HTTP
- Web Development com Zig — Série web
- Integrando com Bancos de Dados — Banco de dados
- gRPC com Zig — gRPC
- Microserviços com Zig — Microserviços