httpz e Zap — Frameworks Web de Alta Performance em Zig

httpz e Zap — Frameworks Web de Alta Performance em Zig

O desenvolvimento web com Zig está ganhando tração graças a frameworks como httpz e Zap que demonstram performance excepcional. Esses frameworks aproveitam o modelo de execução do Zig — sem garbage collector, com controle fino de memória e suporte nativo a I/O assíncrono — para criar servidores HTTP que rivalizam com as soluções mais rápidas em C e Rust.

httpz — O Framework Web Ergonômico

O httpz (também chamado http.zig) é o framework web mais popular do ecossistema Zig. Projetado para ser simples, rápido e idiomático, ele oferece uma API familiar para quem vem de frameworks como Express.js ou Gin.

Características Principais

  • Roteamento rápido com suporte a parâmetros e wildcards
  • Middleware composável
  • WebSockets integrados
  • Streaming de request e response
  • TLS nativo
  • Performance superior a frameworks Node.js e Python

Hello World com httpz

const std = @import("std");
const httpz = @import("httpz");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var server = try httpz.Server(void).init(allocator, .{
        .port = 8080,
    }, {});
    defer server.deinit();

    server.router().get("/", struct {
        fn handler(_: void, response: *httpz.Response) !void {
            response.body = "Olá, mundo!";
        }
    }.handler);

    std.debug.print("Servidor rodando em http://localhost:8080\n", .{});
    try server.listen();
}

Roteamento

O httpz suporta rotas com parâmetros, grupos e métodos HTTP:

const router = server.router();

// Rotas básicas
router.get("/", handleHome);
router.get("/sobre", handleSobre);

// Parâmetros de rota
router.get("/usuarios/:id", handleUsuario);
router.get("/posts/:slug", handlePost);

// Wildcards
router.get("/arquivos/*", handleArquivos);

// Métodos HTTP
router.post("/api/usuarios", criarUsuario);
router.put("/api/usuarios/:id", atualizarUsuario);
router.delete("/api/usuarios/:id", removerUsuario);

// Grupos de rotas
var api = router.group("/api/v1");
api.get("/produtos", listarProdutos);
api.post("/produtos", criarProduto);
api.get("/produtos/:id", obterProduto);

Middleware

// Middleware de logging
fn logMiddleware(
    comptime Handler: type,
) httpz.Middleware(Handler) {
    return struct {
        pub fn handle(
            _: void,
            request: *httpz.Request,
            response: *httpz.Response,
            next: anytype,
        ) !void {
            const inicio = std.time.milliTimestamp();
            try next.handle(request, response);
            const duracao = std.time.milliTimestamp() - inicio;
            std.debug.print("{s} {s} - {}ms\n", .{
                @tagName(request.method),
                request.path,
                duracao,
            });
        }
    };
}

// Middleware de CORS
fn corsMiddleware(response: *httpz.Response) void {
    response.headers.put("Access-Control-Allow-Origin", "*");
    response.headers.put("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
    response.headers.put("Access-Control-Allow-Headers", "Content-Type, Authorization");
}

Respostas JSON

const Produto = struct {
    id: u32,
    nome: []const u8,
    preco: f64,
    estoque: u32,
};

fn listarProdutos(_: void, response: *httpz.Response) !void {
    const produtos = [_]Produto{
        .{ .id = 1, .nome = "Teclado Mecânico", .preco = 349.90, .estoque = 50 },
        .{ .id = 2, .nome = "Mouse Gamer", .preco = 199.90, .estoque = 120 },
        .{ .id = 3, .nome = "Monitor 4K", .preco = 2499.90, .estoque = 15 },
    };

    try response.json(&produtos, .{});
}

fn obterProduto(_: void, request: *httpz.Request, response: *httpz.Response) !void {
    const id_str = request.params.get("id") orelse {
        response.status = .bad_request;
        try response.json(.{ .erro = "ID não fornecido" }, .{});
        return;
    };
    _ = id_str;

    // Buscar produto do banco de dados
    try response.json(.{
        .id = 1,
        .nome = "Teclado Mecânico",
        .preco = 349.90,
    }, .{});
}

WebSockets

fn websocketHandler(
    _: void,
    request: *httpz.Request,
    response: *httpz.Response,
) !void {
    var ws = try request.upgradeToWebSocket(response);

    while (true) {
        const msg = try ws.receive();
        switch (msg.type) {
            .text => {
                try ws.send(.{ .text = msg.data });
            },
            .close => break,
            else => {},
        }
    }
}

Zap — Performance Extrema

O Zap é um framework web focado em performance absoluta, construído sobre o facil.io (uma biblioteca C de I/O assíncrono). Ele frequentemente aparece no topo de benchmarks de frameworks web.

Características do Zap

  • I/O assíncrono baseado em epoll/kqueue
  • Zero alocações no hot path
  • Event loop otimizado
  • Performance comprovada em benchmarks como TechEmpower

Hello World com Zap

const std = @import("std");
const zap = @import("zap");

fn onRequest(r: zap.Request) !void {
    if (r.path) |path| {
        if (std.mem.eql(u8, path, "/")) {
            try r.sendBody("Olá do Zap!");
            return;
        }
    }
    try r.sendBody("404 - Não encontrado");
}

pub fn main() !void {
    var listener = zap.HttpListener.init(.{
        .port = 3000,
        .on_request = onRequest,
        .log = true,
    });

    try listener.listen();
    std.debug.print("Servidor Zap em http://localhost:3000\n", .{});
    zap.start(.{
        .threads = 4,
        .workers = 2,
    });
}

Roteamento no Zap

var router = zap.Router.init(allocator, .{
    .num_threads = 4,
    .on_request = onRequest,
});

router.handle_func("/api/usuarios", handleUsuarios);
router.handle_func("/api/produtos", handleProdutos);
router.handle_func("/health", handleHealth);

Benchmarks e Performance

Ambos os frameworks demonstram performance impressionante:

FrameworkReq/s (hello world)Latência p99Linguagem
Zap~2.500.0000.1msZig
httpz~1.800.0000.2msZig
Actix Web~1.500.0000.3msRust
Gin~800.0000.5msGo
Express~50.0005msNode.js
Flask~8.00025msPython

Esses números variam conforme o hardware e a complexidade do payload, mas demonstram a vantagem fundamental de linguagens sem GC e com controle de memória.

Construindo uma API REST Completa

Exemplo de API REST com httpz usando drivers de banco de dados:

const std = @import("std");
const httpz = @import("httpz");

const AppState = struct {
    allocator: std.mem.Allocator,
    // db: *Database,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    const state = AppState{
        .allocator = allocator,
    };

    var server = try httpz.Server(AppState).init(allocator, .{
        .port = 8080,
        .num_threads = 8,
    }, state);
    defer server.deinit();

    const router = server.router();

    // Rotas da API
    router.get("/api/health", handleHealth);
    router.get("/api/usuarios", listarUsuarios);
    router.post("/api/usuarios", criarUsuario);
    router.get("/api/usuarios/:id", obterUsuario);

    std.debug.print("API rodando em http://localhost:8080\n", .{});
    try server.listen();
}

fn handleHealth(_: AppState, response: *httpz.Response) !void {
    try response.json(.{ .status = "ok", .versao = "1.0.0" }, .{});
}

Qual Escolher?

  • httpz: Melhor para a maioria dos projetos. API mais ergonômica, documentação mais completa e comunidade maior. Ideal para APIs REST, aplicações web e microsserviços.

  • Zap: Melhor quando performance absoluta é prioridade. Ideal para serviços de alta carga, proxies reversos e situações onde cada milissegundo conta.

Próximos Passos

Explore as bibliotecas de clientes HTTP para consumir APIs, os drivers de banco de dados para persistência, e as bibliotecas JSON para serialização. Veja como empresas como Cloudflare usam Zig para serviços web em produção. Para receitas práticas de servidor HTTP, consulte nossas receitas.

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.