std.http.Client em Zig — Referência e Exemplos

std.http.Client — Cliente HTTP

O std.http.Client permite fazer requisições HTTP/1.1 a servidores remotos. Ele suporta todos os métodos HTTP, cabeçalhos customizados, envio de corpo (payload), redirecionamentos automáticos e TLS para conexões HTTPS. É a ferramenta padrão para consumir APIs REST, baixar arquivos e comunicar com serviços web.

Visão Geral

const std = @import("std");
const http = std.http;
const Client = http.Client;

Criação e Destruição

// O Client é uma struct que requer apenas um allocator
var client = http.Client{ .allocator = allocator };
defer client.deinit();

Funções Principais

fetch

A principal função para fazer requisições:

pub fn fetch(self: *Client, options: FetchOptions) !FetchResult

FetchOptions

pub const FetchOptions = struct {
    // URL ou URI pré-parseado
    location: Location,

    // Método HTTP (padrão: GET)
    method: http.Method = .GET,

    // Cabeçalhos extras
    headers: Headers = .{},
    extra_headers: []const http.Header = &.{},

    // Corpo da requisição (para POST, PUT, etc.)
    payload: ?[]const u8 = null,

    // Onde armazenar a resposta
    response_storage: ResponseStorage,

    // Seguir redirecionamentos
    max_redirects: u32 = 3,

    // ...
};

FetchResult

pub const FetchResult = struct {
    status: http.Status,
    // ...
};

Exemplo 1: Consumindo API REST com GET

const std = @import("std");

const Post = struct {
    userId: u32,
    id: u32,
    title: []const u8,
    body: []const u8,
};

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

    var client = std.http.Client{ .allocator = allocator };
    defer client.deinit();

    var body = std.ArrayList(u8).init(allocator);
    defer body.deinit();

    // GET para API pública
    const result = try client.fetch(.{
        .location = .{ .url = "https://jsonplaceholder.typicode.com/posts/1" },
        .response_storage = .{ .dynamic = &body },
    });

    const stdout = std.io.getStdOut().writer();
    try stdout.print("Status: {d}\n", .{@intFromEnum(result.status)});

    if (result.status == .ok) {
        // Desserializa o JSON
        const parsed = try std.json.parseFromSlice(
            Post,
            allocator,
            body.items,
            .{ .ignore_unknown_fields = true },
        );
        defer parsed.deinit();

        const post = parsed.value;
        try stdout.print("Título: {s}\n", .{post.title});
        try stdout.print("Autor (userId): {d}\n", .{post.userId});
    }
}

Exemplo 2: POST com Payload JSON

const std = @import("std");

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

    var client = std.http.Client{ .allocator = allocator };
    defer client.deinit();

    // Prepara o payload JSON
    const dados = .{
        .title = "Novo Post",
        .body = "Conteúdo do post criado via Zig",
        .userId = @as(u32, 1),
    };

    const payload = try std.json.stringifyAlloc(allocator, dados, .{});
    defer allocator.free(payload);

    var body = std.ArrayList(u8).init(allocator);
    defer body.deinit();

    // Faz a requisição POST
    const result = try client.fetch(.{
        .location = .{ .url = "https://jsonplaceholder.typicode.com/posts" },
        .method = .POST,
        .headers = .{
            .content_type = .{ .override = "application/json" },
        },
        .payload = payload,
        .response_storage = .{ .dynamic = &body },
    });

    const stdout = std.io.getStdOut().writer();
    try stdout.print("Status: {d} ({s})\n", .{
        @intFromEnum(result.status),
        result.status.phrase() orelse "?",
    });
    try stdout.print("Resposta:\n{s}\n", .{body.items});
}

Exemplo 3: Download de Arquivo

const std = @import("std");

fn downloadFile(
    allocator: std.mem.Allocator,
    url: []const u8,
    caminho_destino: []const u8,
) !usize {
    var client = std.http.Client{ .allocator = allocator };
    defer client.deinit();

    var body = std.ArrayList(u8).init(allocator);
    defer body.deinit();

    const result = try client.fetch(.{
        .location = .{ .url = url },
        .response_storage = .{ .dynamic = &body },
    });

    if (result.status != .ok) {
        return error.HttpError;
    }

    // Salva em arquivo
    const arquivo = try std.fs.cwd().createFile(caminho_destino, .{});
    defer arquivo.close();
    try arquivo.writeAll(body.items);

    return body.items.len;
}

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

    const stdout = std.io.getStdOut().writer();

    const url = "https://example.com/";
    const destino = "/tmp/exemplo.html";

    try stdout.print("Baixando {s}...\n", .{url});

    if (downloadFile(allocator, url, destino)) |bytes| {
        try stdout.print("Download completo: {d} bytes salvos em {s}\n", .{
            bytes, destino,
        });
    } else |err| {
        try stdout.print("Erro no download: {}\n", .{err});
    }
}

Padrões Comuns

Cabeçalhos de Autenticação

const result = try client.fetch(.{
    .location = .{ .url = url },
    .extra_headers = &.{
        .{ .name = "Authorization", .value = "Bearer meu_token_aqui" },
        .{ .name = "Accept", .value = "application/json" },
    },
    .response_storage = .{ .dynamic = &body },
});

Tratamento de Erros HTTP

const result = try client.fetch(.{ ... });
switch (result.status) {
    .ok, .created => { /* sucesso */ },
    .unauthorized => { /* re-autenticar */ },
    .not_found => { /* recurso não existe */ },
    else => { /* erro genérico */ },
}

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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