req.headers.append em Zig: Como Corrigir no std.http.Client

Se um exemplo com try req.headers.append("Nome", "valor") parou de compilar, a correção mais comum nas versões recentes do Zig é não alterar os headers depois de criar a requisição. Passe os cabeçalhos como uma lista de std.http.Header em extra_headers, normalmente usando std.http.Client.fetch. A API HTTP da biblioteca padrão mudou várias vezes antes do Zig 1.0, por isso muitos snippets antigos encontrados em fóruns e repositórios já não correspondem ao release instalado.

A substituição direta costuma ser esta:

// Exemplo antigo encontrado na internet:
try req.headers.append("User-Agent", "minha-app/1.0");
try req.headers.append("Accept", "application/json");
// Padrão atual com fetch:
const result = try client.fetch(.{
    .location = .{ .url = "https://api.example.com/dados" },
    .extra_headers = &.{
        .{ .name = "User-Agent", .value = "minha-app/1.0" },
        .{ .name = "Accept", .value = "application/json" },
    },
    .response_storage = .{ .dynamic = &body },
});

Este guia explica por que o erro acontece, como migrar GET, POST, Bearer token e API key, e quais detalhes verificar quando o código ainda falha.

Resposta rápida

Código ou objetivoCaminho recomendado
req.headers.append(...) não existePasse extra_headers = &.{ ... } nas opções da operação HTTP
GET simplesUse client.fetch com location, extra_headers e response_storage
POST JSONUse method = .POST, payload e o header tipado de content_type
Bearer token dinâmicoMonte o valor em um buffer que continue válido durante a chamada
Header conhecido da stdlibPrefira o campo tipado em headers quando ele existir
Header customizadoUse extra_headers com pares .name e .value
Exemplo de outra versãoConfirme a versão com zig version e consulte a documentação correspondente

Para uma visão completa do cliente, veja o tutorial de std.http.Client. Se você precisa apenas copiar uma requisição pronta, consulte as receitas de HTTP GET em Zig e HTTP POST em Zig.

Por que req.headers.append deixou de funcionar?

Zig ainda está antes da versão 1.0, então APIs da biblioteca padrão podem mudar entre releases. O módulo std.http passou por redesigns que alteraram a criação da requisição, os tipos de headers, a sequência de envio e as abstrações de I/O.

Um exemplo antigo pode falhar com mensagens parecidas com estas:

error: no field named 'headers' in struct 'http.Client.Request'
error: no field or member function named 'append'
error: expected type '[]const http.Header', found ...

Esses erros não significam necessariamente que headers customizados foram removidos. Em geral, significa que eles agora são fornecidos como configuração da operação, em vez de serem anexados por mutação depois que o objeto foi criado.

A primeira etapa do diagnóstico é sempre registrar o release real:

zig version

Depois, compare o código com a documentação desse release. Não misture um exemplo de Zig 0.12, outro de 0.14 e tipos de 0.16 no mesmo arquivo. Essa combinação é uma das causas mais frequentes de erros em std.http.

GET com headers usando extra_headers

O exemplo abaixo faz um GET, envia User-Agent e Accept, limita a memória disponível para a resposta e verifica o status HTTP.

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

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

    const result = try client.fetch(.{
        .location = .{
            .url = "https://api.github.com/repos/ziglang/zig",
        },
        .extra_headers = &.{
            .{ .name = "User-Agent", .value = "zig-exemplo/1.0" },
            .{ .name = "Accept", .value = "application/vnd.github+json" },
        },
        .response_storage = .{ .dynamic = &body },
    });

    if (result.status != .ok) {
        std.log.err("API respondeu com status {d}", .{
            @intFromEnum(result.status),
        });
        return error.HttpStatusInesperado;
    }

    std.debug.print("{s}\n", .{body.items});
}

extra_headers recebe uma slice de std.http.Header. A sintaxe &.{ ... } cria uma lista literal adequada para valores fixos. Para integrações reais, mantenha um limite operacional para a resposta; a forma exata desse limite depende da API de response_storage disponível no seu release e da estratégia usada pelo programa.

Header tipado ou extra_headers?

As opções de fetch normalmente separam headers conhecidos de headers arbitrários. Um Content-Type pode ter um campo tipado, enquanto X-API-Key, Idempotency-Key ou um header específico do fornecedor entra em extra_headers.

Exemplo de POST JSON:

const payload =
    \\{"nome":"Ana","ativo":true}
;

const result = try client.fetch(.{
    .location = .{ .url = "https://api.example.com/usuarios" },
    .method = .POST,
    .headers = .{
        .content_type = .{ .override = "application/json" },
    },
    .extra_headers = &.{
        .{ .name = "Accept", .value = "application/json" },
        .{ .name = "User-Agent", .value = "minha-api-client/1.0" },
    },
    .payload = payload,
    .response_storage = .{ .dynamic = &body },
});

A regra prática é:

  1. use o campo tipado quando a versão instalada o oferecer;
  2. use extra_headers para extensões e valores sem campo próprio;
  3. não envie duas versões conflitantes do mesmo header;
  4. confira se o nome e o valor continuam vivos durante a operação.

Authorization Bearer com valor dinâmico

Tokens normalmente chegam por variável de ambiente, arquivo seguro ou serviço de credenciais. Como o valor completo precisa incluir o prefixo Bearer , você pode montá-lo em um buffer local.

var authorization_buffer: [2048]u8 = undefined;
const authorization = try std.fmt.bufPrint(
    &authorization_buffer,
    "Bearer {s}",
    .{token},
);

const result = try client.fetch(.{
    .location = .{ .url = "https://api.example.com/conta" },
    .extra_headers = &.{
        .{ .name = "Authorization", .value = authorization },
        .{ .name = "Accept", .value = "application/json" },
    },
    .response_storage = .{ .dynamic = &body },
});

O buffer precisa permanecer válido até fetch terminar. Não devolva uma slice apontando para uma variável local que já saiu de escopo e não reutilize o mesmo buffer em operações concorrentes.

Também evite imprimir o header em logs. Em produção, registre que a autenticação foi configurada, mas nunca o token. O guia de configuração segura e segredos em Zig detalha esse cuidado.

API key e headers específicos

Para uma chave enviada em X-API-Key, a configuração é direta:

const result = try client.fetch(.{
    .location = .{ .url = "https://api.example.com/relatorio" },
    .extra_headers = &.{
        .{ .name = "X-API-Key", .value = api_key },
        .{ .name = "Accept", .value = "application/json" },
    },
    .response_storage = .{ .dynamic = &body },
});

Antes de implementar, confirme como o fornecedor espera receber a credencial. Algumas APIs usam Authorization: Bearer, outras X-API-Key, e sistemas legados podem exigir nomes próprios. Não mova uma chave para a query string apenas para contornar um problema de header: URLs aparecem com mais facilidade em logs, histórico, métricas e traces.

Migração de um exemplo antigo passo a passo

Considere este padrão antigo:

var req = try client.request(.GET, uri, .{
    .allocator = allocator,
}, .{});
defer req.deinit();

try req.headers.append("User-Agent", "app/1.0");
try req.headers.append("Accept", "application/json");

try req.start();
try req.finish();
try req.wait();

Para migrá-lo sem carregar partes incompatíveis da API antiga:

  1. mantenha a criação e o deinit do std.http.Client;
  2. substitua a sequência manual por client.fetch quando ela atender ao caso;
  3. mova URL e método para as opções de fetch;
  4. converta cada append(nome, valor) em um std.http.Header;
  5. defina onde o corpo da resposta será armazenado;
  6. valide result.status antes de fazer parse do body;
  7. rode zig fmt e os testes do projeto.

O equivalente fica conceitualmente assim:

const result = try client.fetch(.{
    .location = .{ .url = url },
    .method = .GET,
    .extra_headers = &.{
        .{ .name = "User-Agent", .value = "app/1.0" },
        .{ .name = "Accept", .value = "application/json" },
    },
    .response_storage = .{ .dynamic = &body },
});

Essa migração é melhor do que tentar encontrar um novo lugar para chamar append, pois evita misturar o ciclo de vida antigo da requisição com as opções atuais.

O que verificar se extra_headers também der erro

1. A versão do exemplo não é a sua

Confirme:

zig version

Se o projeto fixa uma versão em CI, container, mise, asdf ou zigup, use a mesma localmente. “Funciona na minha máquina” costuma significar apenas que duas versões diferentes estão compilando APIs diferentes.

2. O tipo esperado mudou

Leia a assinatura de FetchOptions na documentação ou no código da stdlib instalada. O compilador do Zig normalmente mostra o tipo esperado e a origem da declaração. Siga essa trilha em vez de aplicar casts aleatórios.

3. O valor do header não vive tempo suficiente

Um header guarda slices; ele não necessariamente copia todos os valores. Strings literais vivem pelo programa inteiro, mas valores formatados precisam permanecer válidos durante a chamada.

4. Existem headers duplicados

Evite definir Content-Type tanto em .headers.content_type quanto em .extra_headers. Duplicidade pode causar rejeição pelo servidor ou comportamento ambíguo em proxies.

5. Você precisa da API de baixo nível

fetch resolve GET, POST e boa parte das integrações REST. Streaming de upload, controle fino da conexão ou protocolos long-lived podem exigir APIs de nível mais baixo, que são mais sensíveis à versão. Nesse caso, consulte a documentação exata do release e mantenha esse código isolado em um adaptador pequeno.

Checklist para headers HTTP em Zig

  • Executei zig version e usei documentação do mesmo release.
  • Troquei req.headers.append por opções compatíveis com a versão atual.
  • Usei extra_headers para headers customizados.
  • Usei o campo tipado para Content-Type quando disponível.
  • Mantive buffers dinâmicos vivos durante a requisição.
  • Não registrei token, cookie, API key ou header Authorization.
  • Validei o status HTTP antes de interpretar JSON.
  • Limitei o tamanho da resposta conforme o risco da integração.
  • Reutilizei std.http.Client quando faço várias chamadas.
  • Fixei a versão do Zig no CI e no ambiente de desenvolvimento.

Conclusão

O erro em req.headers.append quase sempre é um sinal de exemplo desatualizado, não de ausência de suporte a headers no Zig. Para requisições comuns, passe os cabeçalhos em extra_headers dentro das opções de std.http.Client.fetch; use campos tipados para headers conhecidos e mantenha valores dinâmicos válidos durante toda a chamada.

A lição mais importante é não migrar std.http linha por linha entre versões. Migre a operação inteira — criação, headers, body, armazenamento da resposta e validação de status — usando uma única API coerente com o release do projeto.

Para continuar, leia Zig HTTP Client: GET, POST, JSON e APIs REST, a receita de HTTP GET com std.http.Client e o guia de tratamento de erros em Zig.

Continue aprendendo Zig

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