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 ​
| Protocol | Status | Transport | Notes |
|---|---|---|---|
| HTTP/1.0 | ✅ Full | TCP | Legacy support |
| HTTP/1.1 | ✅ Full | TCP/TLS | Default protocol |
| HTTP/2 | ✅ Client Runtime + Primitives | TCP/TLS | High-level client request execution path plus full framing/HPACK/stream primitives |
| HTTP/3 | ✅ Client Runtime + Primitives | QUIC/UDP | High-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 ​
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) ​
| Field | Type | Default | Description |
|---|---|---|---|
base_url | ?[]const u8 | null | Base URL prepended to all requests. |
timeouts | Timeouts | {} | Connection and read/write timeouts. |
retry_policy | RetryPolicy | {} | Configuration for automatic retries. |
redirect_policy | RedirectPolicy | {} | Configuration for handling redirects. |
default_headers | ?[]const [2][]const u8 | null | Headers added to every request. |
user_agent | []const u8 | "httpx.zig/0.0.7" | User-Agent header value. |
max_response_size | usize | 100MB | Maximum allowed response body size. |
follow_redirects | bool | true | Whether to automatically follow redirects. |
verify_ssl | bool | true | Whether to verify SSL certificates. |
http2_enabled | bool | false | Enable high-level HTTP/2 execution path for client requests. |
http3_enabled | bool | false | Enable high-level HTTP/3 execution path over UDP/QUIC stream framing. |
http2_settings | Http2Settings | {} | HTTP/2 SETTINGS values sent during connection setup (header_table_size, max_frame_size, etc.). |
http3_settings | Http3Settings | {} | HTTP/3/QPACK settings sent on the control stream (max_field_section_size, qpack_max_table_capacity, qpack_blocked_streams, etc.). |
pool_max_connections | u32 | 20 | Maximum connections in the pool. |
pool_max_per_host | u32 | 5 | Maximum connections to a single host. |
Methods ​
request ​
Makes a generic HTTP request.
pub fn request(self: *Self, method: Method, url: []const u8, options: RequestOptions) !Responsesend (alias) ​
Alias for request with shorter naming.
pub fn send(self: *Self, method: Method, url: []const u8, options: RequestOptions) !ResponseShort aliases ​
pub fn del(self: *Self, url: []const u8, options: RequestOptions) !Response
pub fn opts(self: *Self, url: []const u8, options: RequestOptions) !ResponseConvenience Methods ​
| Method | Description |
|---|---|
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 |
Cookie Jar API ​
The client keeps an in-memory cookie jar and automatically:
- Adds a
Cookieheader to outgoing requests. - Stores
Set-Cookievalues from incoming responses.
| Method | Description |
|---|---|
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 ​
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:
- Simple Get
- Simple Get Deserialize
- Post JSON
- Custom Headers
- Concurrent Requests
- Connection Pool
- Interceptors
- Cookies Demo
- Simplified API Aliases
Request Options (RequestOptions) ​
Per-request overrides for configuration.
| Field | Type | Default | Description |
|---|---|---|---|
headers | ?[]const [2][]const u8 | null | Additional headers for this request. |
body | ?[]const u8 | null | Raw request body. |
json | ?[]const u8 | null | JSON string body (sets Content-Type). |
timeout_ms | ?u64 | null | Request-specific timeout. |
follow_redirects | ?bool | null | Override client redirect setting. |
Response ​
The Response struct contains the server's response.
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 ​
| Method | Description |
|---|---|
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 ​
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 ​
// 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 ​
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:
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 ​
- Protocol API - HTTP/2, HTTP/3, HPACK, QPACK
- Connection Pool - Connection pooling
- Concurrency - Parallel requests
- Client Guide - Usage guide
