Skip to content

Client API ​

The httpx.zig client provides a high-level HTTP client for making requests over HTTP/1.0, HTTP/1.1, HTTP/2, and HTTP/3. HTTPS is supported via Zig's standard library TLS (std.crypto.tls) for HTTP/1.x and HTTP/2.

Protocol Support ​

ProtocolStatusTransportNotes
HTTP/1.0✅ FullTCPLegacy support
HTTP/1.1✅ FullTCP/TLSDefault protocol
HTTP/2✅ Client Runtime + PrimitivesTCP/TLSHigh-level client request execution path plus full framing/HPACK/stream primitives
HTTP/3✅ Client Runtime + PrimitivesQUIC/UDPHigh-level client runtime over UDP + QUIC/HTTP3/QPACK primitives (suitable for local/integration endpoints)

HTTP/3 runtime mode is available in the high-level client and uses QUIC packet/stream framing primitives directly. Interoperability with endpoints that require full TLS-in-QUIC handshake negotiation may vary depending on deployment expectations.

The protocol module provides HTTP/2 and HTTP/3 building blocks (HPACK/QPACK, framing, and transport primitives). See Protocol API for details.

Client ​

The Client struct is the main entry point for making requests. It manages connection pooling, cookies, and interceptors.

Initialization ​

zig
const httpx = @import("httpx");

// Initialize with default configuration
var client = httpx.Client.init(allocator);
defer client.deinit();

// Initialize with custom configuration
var client = httpx.Client.initWithConfig(allocator, .{
    .base_url = "https://api.example.com",
    .user_agent = "my-app/1.0",
});
defer client.deinit();

Configuration (ClientConfig) ​

FieldTypeDefaultDescription
base_url?[]const u8nullBase URL prepended to all requests.
timeoutsTimeouts{}Connection and read/write timeouts.
retry_policyRetryPolicy{}Configuration for automatic retries.
redirect_policyRedirectPolicy{}Configuration for handling redirects.
default_headers?[]const [2][]const u8nullHeaders added to every request.
user_agent[]const u8"httpx.zig/0.0.7"User-Agent header value.
max_response_sizeusize100MBMaximum allowed response body size.
follow_redirectsbooltrueWhether to automatically follow redirects.
verify_sslbooltrueWhether to verify SSL certificates.
http2_enabledboolfalseEnable high-level HTTP/2 execution path for client requests.
http3_enabledboolfalseEnable high-level HTTP/3 execution path over UDP/QUIC stream framing.
http2_settingsHttp2Settings{}HTTP/2 SETTINGS values sent during connection setup (header_table_size, max_frame_size, etc.).
http3_settingsHttp3Settings{}HTTP/3/QPACK settings sent on the control stream (max_field_section_size, qpack_max_table_capacity, qpack_blocked_streams, etc.).
pool_max_connectionsu3220Maximum connections in the pool.
pool_max_per_hostu325Maximum connections to a single host.

Methods ​

request ​

Makes a generic HTTP request.

zig
pub fn request(self: *Self, method: Method, url: []const u8, options: RequestOptions) !Response

send (alias) ​

Alias for request with shorter naming.

zig
pub fn send(self: *Self, method: Method, url: []const u8, options: RequestOptions) !Response

Short aliases ​

zig
pub fn del(self: *Self, url: []const u8, options: RequestOptions) !Response
pub fn opts(self: *Self, url: []const u8, options: RequestOptions) !Response

Convenience Methods ​

MethodDescription
get(url, options)HTTP GET request
fetch(url, options)Alias for GET request
post(url, options)HTTP POST request
put(url, options)HTTP PUT request
delete(url, options)HTTP DELETE request
del(url, options)Alias for HTTP DELETE request
patch(url, options)HTTP PATCH request
head(url, options)HTTP HEAD request
httpOptions(url, options)HTTP OPTIONS request
options(url, options)Alias for HTTP OPTIONS request
opts(url, options)Alias for HTTP OPTIONS request
send(method, url, options)Alias for generic request
addInterceptor(interceptor)Add request/response interceptor

The client keeps an in-memory cookie jar and automatically:

  • Adds a Cookie header to outgoing requests.
  • Stores Set-Cookie values from incoming responses.
MethodDescription
setCookie(name, value)Add or replace a cookie in the jar
getCookie(name)Read a cookie value
removeCookie(name)Remove one cookie
clearCookies()Remove all cookies
hasCookie(name)Check whether a cookie exists
cookieCount()Get total cookie count

Quick Examples ​

zig
const httpx = @import("httpx");

var client = httpx.Client.init(allocator);
defer client.deinit();

// Simple GET
const response = try client.get("https://api.example.com/users", .{});
defer response.deinit();
std.debug.print("Status: {d}\n", .{response.status.code});
std.debug.print("Body: {s}\n", .{response.text() orelse ""});

// POST with JSON
const json_response = try client.post("https://api.example.com/users", .{
    .json = "{\"name\": \"John\", \"email\": \"john@example.com\"}",
});
defer json_response.deinit();

// Custom headers
const auth_response = try client.get("https://api.example.com/protected", .{
    .headers = &.{
        .{ "Authorization", "Bearer token123" },
        .{ "X-Custom-Header", "value" },
    },
});
defer auth_response.deinit();

// With timeout
const timeout_response = try client.get("https://slow-api.com/data", .{
    .timeout_ms = 30000, // 30 seconds
});
defer timeout_response.deinit();

Client Usage Recipes ​

For complete copy/paste demos, see these example pages:

Request Options (RequestOptions) ​

Per-request overrides for configuration.

FieldTypeDefaultDescription
headers?[]const [2][]const u8nullAdditional headers for this request.
body?[]const u8nullRaw request body.
json?[]const u8nullJSON string body (sets Content-Type).
timeout_ms?u64nullRequest-specific timeout.
follow_redirects?boolnullOverride client redirect setting.

Response ​

The Response struct contains the server's response.

zig
pub const Response = struct {
    version: Version,
    status: Status,
    headers: Headers,
    body: ?[]const u8,

    pub fn deinit(self: *Response) void
    pub fn ok(self: *const Response) bool
    pub fn isRedirect(self: *const Response) bool
    pub fn isError(self: *const Response) bool
    pub fn text(self: *const Response) ?[]const u8
    pub fn json(self: *const Response, comptime T: type) !T
    pub fn location(self: *const Response) ?[]const u8
    pub fn contentType(self: *const Response) ?[]const u8
    pub fn contentLength(self: *const Response) ?u64
    pub fn isChunked(self: *const Response) bool
    pub fn header(self: *const Response, name: []const u8) ?[]const u8
};

Response Methods ​

MethodDescription
deinit()Free response resources
header(name)Get header value by name
ok()Status 200-299
isRedirect()Status 300-399
isError()Status 400-599
text()Get response body text
json(T)Parse response body as JSON

Interceptors ​

Interceptors allow you to modify requests before they are sent or responses before they are returned.

Structure ​

zig
pub const RequestInterceptor = *const fn (*Request, ?*anyopaque) anyerror!void;
pub const ResponseInterceptor = *const fn (*Response, ?*anyopaque) anyerror!void;

pub const Interceptor = struct {
    request_fn: ?RequestInterceptor = null,
    response_fn: ?ResponseInterceptor = null,
    context: ?*anyopaque = null,
};

Both request_fn and response_fn are optional. You can register only one callback or both.

Usage ​

zig
// Logging interceptor
fn logRequest(request: *httpx.Request, _: ?*anyopaque) !void {
    std.debug.print("Request: {s} {s}\n", .{@tagName(request.method), request.uri.path});
}

fn logResponse(response: *httpx.Response, _: ?*anyopaque) !void {
    std.debug.print("Response: {d}\n", .{response.status.code});
}

// Add interceptor
try client.addInterceptor(.{
    .request_fn = logRequest,
    .response_fn = logResponse,
});

// Authentication interceptor with context
const AuthContext = struct {
    token: []const u8,
};

fn addAuth(request: *httpx.Request, ctx: ?*anyopaque) !void {
    if (ctx) |c| {
        const auth: *AuthContext = @ptrCast(@alignCast(c));
        try request.setHeader("Authorization", auth.token);
    }
}

var auth_ctx = AuthContext{ .token = "Bearer secret123" };
try client.addInterceptor(.{
    .request_fn = addAuth,
    .context = &auth_ctx,
});

Error Handling ​

zig
const response = client.get("https://example.com", .{}) catch |err| switch (err) {
    error.ConnectionRefused => {
        std.debug.print("Server not available\n", .{});
        return;
    },
    error.Timeout => {
        std.debug.print("Request timed out\n", .{});
        return;
    },
    error.TlsError => {
        std.debug.print("TLS handshake failed\n", .{});
        return;
    },
    else => return err,
};

Simplified Top-Level Aliases ​

The root module also exposes simple aliases for common client usage:

zig
var a = try httpx.fetch(allocator, "https://example.com");
defer a.deinit();

var b = try httpx.send(allocator, .GET, "https://example.com/health", .{});
defer b.deinit();

var c = try httpx.post(allocator, "https://example.com/items", .{ .json = "{\"name\":\"demo\"}" });
defer c.deinit();

var d = try httpx.delete(allocator, "https://example.com/items/42", .{});
defer d.deinit();

var e = try httpx.opts(allocator, "https://example.com/items", .{});
defer e.deinit();

See Also ​

Released under the MIT License.