christianhelle/openapi2zig
A tool for generating clients and contracts in Zig from OpenAPI specifications
A CLI tool and Zig library that generates type-safe API client code from OpenAPI specifications.
Note: This project provides both a CLI tool for generating Zig code from OpenAPI specs and a library for parsing and working with OpenAPI documents programmatically in Zig.
The fastest way to get started with development is using GitHub Codespaces, which provides a pre-configured development environment with Zig, ZLS (Zig Language Server), and all necessary VS Code extensions.
If you prefer local development with Docker:
Install Zig locally following the official installation guide.
Linux/macOS:
curl -fsSL https://christianhelle.com/openapi2zig/install | bash
Windows (PowerShell):
irm https://christianhelle.com/openapi2zig/install.ps1 | iex
The install scripts will:
Custom installation directory:
# Linux/macOS
INSTALL_DIR=$HOME/.local/bin curl -fsSL https://christianhelle.com/openapi2zig/install | bash
# Windows
irm https://christianhelle.com/openapi2zig/install.ps1 | iex -InstallDir "C:\Tools"
Download the latest release for your platform from the GitHub Releases page:
openapi2zig-linux-x86_64.tar.gz
openapi2zig-macos-x86_64.tar.gz
openapi2zig-macos-aarch64.tar.gz
openapi2zig-windows-x86_64.zip
Extract the archive and add the binary to your PATH.
Install the latest build for Linux from the Snap Store:
snap install --edge openapi2zig
Make sure you have Zig installed (version 0.14 or later).
git clone https://github.com/christianhelle/openapi2zig.git
cd openapi2zig
zig build
The openapi2zig is available as a Docker image on Docker Hub at christianhelle/openapi2zig
.
# Pull the latest image
docker pull christianhelle/openapi2zig
Clone the repository:
git clone https://github.com/christianhelle/openapi2zig.git
cd openapi2zig
Build the project:
zig build
Run tests to verify everything works:
zig build test
The compiled binary will be available in zig-out/bin/openapi2zig
For development builds with debug information:
zig build -Doptimize=Debug
To run tests during development:
zig build test
To check code formatting:
zig fmt --check src/
zig fmt --check build.zig
Build for different targets:
# Windows
zig build -Dtarget=x86_64-windows
# macOS
zig build -Dtarget=x86_64-macos
# Linux ARM64
zig build -Dtarget=aarch64-linux
Note: The CLI interface is currently under development. The tool currently includes OpenAPI parsing functionality and will be extended with code generation capabilities.
openapi2zig generate [options]
Flag | Description |
---|---|
-i , --input <path> |
Path to the OpenAPI Specification file (JSON or YAML). |
-o , --output <path> |
Path to the output directory for the generated Zig code (default: current directory). |
--base-url <url> |
Base URL for the API client (default: server URL from OpenAPI Specification). |
openapi2zig can also be used as a Zig library for parsing OpenAPI/Swagger specifications and generating code programmatically.
Add openapi2zig to your build.zig.zon
:
.{
.name = "my-project",
.version = "0.1.0",
.dependencies = .{
.openapi2zig = .{
.url = "https://github.com/christianhelle/openapi2zig/archive/refs/tags/v1.0.0.tar.gz",
.hash = "12345...", // Replace with actual hash from `zig fetch`
},
},
}
Then in your build.zig
:
const openapi2zig_dep = b.dependency("openapi2zig", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("openapi2zig", openapi2zig_dep.module("openapi2zig"));
const std = @import("std");
const openapi2zig = @import("openapi2zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Read OpenAPI specification
const content = try std.fs.cwd().readFileAlloc(allocator, "api.json", 1024 * 1024);
defer allocator.free(content);
// Detect version
const version = try openapi2zig.detectVersion(allocator, content);
std.debug.print("Detected version: {}\n", .{version});
// Parse to unified document representation
var unified_doc = try openapi2zig.parseToUnified(allocator, content);
defer unified_doc.deinit(allocator);
std.debug.print("API: {s} v{s}\n", .{ unified_doc.info.title, unified_doc.info.version });
// Generate Zig code
const args = openapi2zig.CliArgs{
.input_path = "api.json",
.output_path = null,
.base_url = "https://api.example.com",
};
const generated_code = try openapi2zig.generateCode(allocator, unified_doc, args);
defer allocator.free(generated_code);
// Write generated code to file
try std.fs.cwd().writeFile(.{ .sub_path = "generated.zig", .data = generated_code });
}
detectVersion(allocator, json_content)
- Detect OpenAPI/Swagger versionApiVersion
- Enum representing supported API versions (.v2_0, .v3_0, .v3_1, .Unsupported)parseToUnified(allocator, json_content)
- Parse any supported version to unified representationparseOpenApi(allocator, json_content)
- Parse OpenAPI v3.0 specificallyparseSwagger(allocator, json_content)
- Parse Swagger v2.0 specificallygenerateCode(allocator, unified_doc, args)
- Generate complete Zig code (models + API)generateModels(allocator, unified_doc)
- Generate only model structsgenerateApi(allocator, unified_doc, args)
- Generate only API client functionsconvertOpenApiToUnified(allocator, openapi_doc)
- Convert OpenAPI v3.0 to unified formatconvertSwaggerToUnified(allocator, swagger_doc)
- Convert Swagger v2.0 to unified formatUnifiedDocument
- Common document representation for both OpenAPI and SwaggerOpenApiDocument
- OpenAPI v3.0 specific document structureSwaggerDocument
- Swagger v2.0 specific document structureDocumentInfo
, Schema
, Operation
, etc. - Various OpenAPI componentsBelow is an example of the Zig code generated from an OpenAPI specification.
///////////////////////////////////////////
// Generated Zig structures from OpenAPI
///////////////////////////////////////////
pub const Order = struct {
status: ?[]const u8 = null,
petId: ?i64 = null,
complete: ?bool = null,
id: ?i64 = null,
quantity: ?i64 = null,
shipDate: ?[]const u8 = null,
};
pub const Pet = struct {
status: ?[]const u8 = null,
tags: ?[]const u8 = null,
category: ?[]const u8 = null,
id: ?i64 = null,
name: []const u8,
photoUrls: []const u8,
};
///////////////////////////////////////////
// Generated Zig API client from OpenAPI
///////////////////////////////////////////
const std = @import("std");
/////////////////
// Summary:
// Place an order for a pet
//
// Description:
// Place a new order in the store
//
pub fn placeOrder(allocator: std.mem.Allocator, requestBody: Order) !void {
var client = std.http.Client.init(allocator);
defer client.deinit();
const uri = try std.Uri.parse("https://petstore.swagger.io/api/v3/store/order");
const buf = try allocator.alloc(u8, 1024 * 8);
defer allocator.free(buf);
var req = try client.open(.POST, uri, .{
.server_header_buffer = buf,
});
defer req.deinit();
try req.send();
var str = std.ArrayList(u8).init(allocator);
defer str.deinit();
try std.json.stringify(requestBody, .{}, str.writer());
const body = try std.mem.join(allocator, "", str.items);
req.transfer_encoding = .{ .content_length = body.len };
try req.writeAll(body);
try req.finish();
try req.wait();
}
/////////////////
// Summary:
// Find pet by ID
//
// Description:
// Returns a single pet
//
pub fn getPetById(allocator: std.mem.Allocator, petId: []const u8) !Pet {
var client = std.http.Client { .allocator = allocator };
defer client.deinit();
var header_buffer: [8192]u8 = undefined;
const headers = &[_]std.http.Header{
.{ .name = "Content-Type", .value = "application/json" },
.{ .name = "Accept", .value = "application/json" },
};
const uri_str = try std.fmt.allocPrint(allocator, "https://petstore3.swagger.io/api/v3/pet/{s}", .{petId});
defer allocator.free(uri_str);
const uri = try std.Uri.parse(uri_str);
var req = try client.open(std.http.Method.GET, uri, .{ .server_header_buffer = &header_buffer, .extra_headers = headers });
defer req.deinit();
try req.send();
try req.finish();
try req.wait();
const response = req.response;
if (response.status != .ok) {
return error.ResponseError;
}
const body = try req.reader().readAllAlloc(allocator, 1024 * 1024 * 4);
defer allocator.free(body);
const parsed = try std.json.parseFromSlice(Pet, allocator, body, .{});
defer parsed.deinit();
return parsed.value;
}
git checkout -b feature/amazing-feature
)zig build test
)zig fmt --check src/
)git commit -am 'Add some amazing feature'
)git push origin feature/amazing-feature
)This project follows standard Zig formatting. Use zig fmt
to format your code before committing.
🚧 Under Active Development 🚧
This project is in early development. Current capabilities include:
std.http.Client
Planned features:
This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have questions, please open an issue on GitHub.