openapi2zig
Generate type-safe Zig client code from OpenAPI specifications
A CLI tool written in Zig that generates API client code in Zig from OpenAPI specifications. Supports Swagger v2.0 and OpenAPI v3.0, v3.1, and v3.2 specifications. Generate idiomatic, type-safe Zig client code for your APIs.
Installation
This tool is distributed as a compiled binary for multiple platforms.
Development Environment
Get started with contributing to openapi2zig quickly using our pre-configured development environments.
Development Workflow
Once your development environment is ready:
Build the project:
zig build
Run tests:
zig build test
Run the application:
zig build run
Usage
Command-line Interface
The generate command reads a JSON OpenAPI/Swagger
document from a local file or http/https
URL, auto-detects the spec version, and writes one Zig source file.
openapi2zig generate [options]
Options
| Flag | Description |
|---|---|
-i, --input <PATH_OR_URL> |
OpenAPI/Swagger JSON spec from a file path or URL. Required. |
-o, --output <path> |
Output file for generated Zig code. Defaults to generated.zig. |
--base-url <url> |
Base URL baked into the generated Client; defaults to the spec server URL. |
--resource-wrappers <mode> |
Resource wrapper mode: none, tags, paths, or hybrid. Defaults to paths. |
Examples
openapi2zig generate -i openapi/v3.0/petstore.json -o api.zig --base-url https://petstore3.swagger.io/api/v3
Current Capabilities
- ✅ Swagger v2.0 specification parsing and generation
- ✅ OpenAPI v3.0, v3.1, and v3.2 parsing and generation
- ✅ Local files and remote
http/httpsJSON specs - ✅ Generated
Clientwith base URL, API key, and borrowed headers - ✅ Percent-encoded query parameters and loose response parsing
- ✅ Raw/result helpers that preserve response bodies
- ✅ Bounded raw and typed SSE parser helpers
- ✅ Resource wrapper namespaces generated from paths, tags, or hybrid metadata
Example Generated Code
These snippets match the current generated/generated_v3.zig
style produced by zig build run-generate-v3.
Generated output structure
- Schema declarations such as
Pet,Order, and helper types. - A reusable
Clientthat ownsstd.http.Client. Owned(T),RawResponse,ParseErrorResponse, andApiResult(T)wrappers.- Endpoint triplets:
operation,operationRaw, andoperationResult. - Percent-encoded query parameters and loose JSON response parsing.
- Bounded raw/typed SSE helpers, OpenAI stream helpers when matching operation IDs exist, and default path-based resource wrappers.
- OpenAPI 3.1 composite-schema metadata is stronger than earlier converters, but unsupported ambiguous shapes still use
std.json.Value.
Models
pub const Tag = struct {
id: ?i64 = null,
name: ?[]const u8 = null,
};
pub const Category = struct {
id: ?i64 = null,
name: ?[]const u8 = null,
};
pub const Pet = struct {
status: ?[]const u8 = null,
tags: ?[]const Tag = null,
category: ?Category = null,
id: ?i64 = null,
name: []const u8,
photoUrls: []const []const u8,
};
Client
pub const Client = struct {
allocator: std.mem.Allocator,
io: std.Io,
http: std.http.Client,
api_key: []const u8,
base_url: []const u8 = "https://petstore3.swagger.io/api/v3",
organization: ?[]const u8 = null,
project: ?[]const u8 = null,
default_headers: []const std.http.Header = &.{},
pub fn init(allocator: std.mem.Allocator, io: std.Io, api_key: []const u8) Client {
return .{
.allocator = allocator,
.io = io,
.http = .{ .allocator = allocator, .io = io },
.api_key = api_key,
};
}
pub fn deinit(self: *Client) void {
self.http.deinit();
}
pub fn withBaseUrl(self: *Client, base_url: []const u8) void {
self.base_url = base_url;
}
};
Endpoint helpers
pub fn getPetById(client: *Client, petId: i64) !Owned(Pet) {
var result = try getPetByIdResult(client, petId);
switch (result) {
.ok => |ok| return ok,
.api_error => |*err| {
err.deinit();
return error.ResponseError;
},
.parse_error => |*err| {
err.raw.deinit();
return error.ResponseParseError;
},
}
}
pub fn getPetByIdRaw(client: *Client, petId: i64) !RawResponse {
const allocator = client.allocator;
var uri_buf: std.Io.Writer.Allocating = .init(allocator);
defer uri_buf.deinit();
try uri_buf.writer.print("{s}/pet/{d}", .{ client.base_url, petId });
const payload: ?[]const u8 = null;
return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload);
}
pub fn getPetByIdResult(client: *Client, petId: i64) !ApiResult(Pet) {
return parseRawResponse(Pet, try getPetByIdRaw(client, petId));
}
Calling generated code
var client = api.Client.init(allocator, io, "");
defer client.deinit();
client.withBaseUrl("https://petstore3.swagger.io/api/v3");
var pet = try api.getPetById(&client, 1);
defer pet.deinit();
std.debug.print("pet name: {s}\n", .{pet.value().name});
var wrapped = try api.pet.get(&client, 1);
defer wrapped.deinit();
Contributing
We welcome contributions to openapi2zig! Here's how you can help:
Fork the Repository
Create your own fork of the openapi2zig repository on GitHub.
Create a Feature Branch
Create a new branch for your feature or bug fix.
git checkout -b feature/amazing-feature
Make Your Changes
Implement your feature or fix, ensuring you follow the coding standards.
Test Your Changes
Run the test suite and ensure all tests pass.
zig build test
Submit a Pull Request
Push your changes and create a pull request for review.
Code Style
This project follows standard Zig formatting. Use
zig fmt to format your code before committing.
zig fmt --check src/
zig fmt --check build.zig