The free, open-source OpenAPI 3.x → TypeScript, Go, Rust, and WASM SDK generator. Production-ready clients in 60 seconds — auth, retry, idempotency, validation, SSE, and middleware built in.
For backend & platform teams shipping public APIs who are tired of hand-rolling SDK clients.
curl -sSL https://specforge.deepwhaleai.com/install | sh
Most OpenAPI generators leave teams building runtime infrastructure by hand. specforge delivers a complete, production-ready SDK — not just types.
| vs openapi-generator, swagger-codegen, fern, stainless, speakeasy | specforge |
|---|---|
Incomplete types — nullable, oneOf, allOf produce broken or any-typed output | Full composition: allOf merging, oneOf type guards, discriminator mapping, nullable propagation |
| No runtime — you get types but no client, auth, retry, or error handling | Production-ready runtime: auth providers, exponential backoff, pagination, middleware, idempotency, SSE streaming |
| Single language — each generator is a silo with different behavior | One IR, four targets: TypeScript, Go, Rust, and WASM plugins share the same resolved spec |
| No validation — generated code trusts the server blindly | Runtime request/response validation catches contract violations in dev and tests |
| No testing — you write mock servers by hand | specforge test generates mock server tests from example responses |
| No documentation — separate tools for API docs | specforge docs generates a static HTML documentation site |
| No CI integration — manual diffing and linting | specforge diff detects breaking changes, specforge check lints specs, GitHub Action for one-line CI |
| Slow CLI on large specs (multi-minute runs on GitHub / Stripe) | 12× faster than OpenAPI Generator on real-world specs (1,431-schema Stripe, 965-schema GitHub). See benchmarks. |
A four-stage pipeline that turns one OpenAPI spec into four production-ready SDKs — with the same auth, retry, and pagination logic across every language.
$ref, merges allOf, and lowers 3.1→3.0 quirks.Each stage is independent and testable. The IR is documented in assets/ir-schema.json — build custom emitters against it with specforge emit.
# TypeScript — native fetch, dual ESM/CJS package, tree-shakeable specforge generate openapi.yaml -o ./sdk-ts -l ts # Go — stdlib net/http only, zero third-party deps specforge generate openapi.yaml -o ./sdk-go -l go -n github.com/acme/widget-go # Rust — reqwest + serde + tokio, async-first specforge generate openapi.yaml -o ./sdk-rs -l rust -n widget_sdk # Lint a spec (strict mode treats warnings as errors) specforge check openapi.yaml --strict # Detect breaking changes — exit 1 on breaking, perfect for CI gates specforge diff v1.yaml v2.yaml
Handles real-world OpenAPI specs at scale — GitHub (~965 schemas / 1209 ops) and Stripe (~1431 / 587).
Handles both spec versions transparently. 3.1 type arrays, $ref siblings, and numeric exclusiveMinimum are auto-converted for parsing.
Named types stay named — no exponential inlining blow-ups. Self-referential and mutual $ref cycles are safe by construction.
allOf merges properties (last-wins, required union). oneOf/anyOf generate type guards. Discriminator mapping preserved.
allOf with one $ref: Go emits embedded structs, Rust emits #[serde(flatten)]. Proper composition, not flat merging.
IndexMap preserves spec order. Same spec + same version = identical output. Bit-stable for caching and diffing.
8 configurable rules (duplicate operation IDs, missing descriptions, unused schemas) with .specforge.yaml config.
specforge diff compares two specs: removed operations, new required parameters, type changes. Exit code 1 for CI gates.
Auth providers, exponential backoff, pagination helpers, concurrency control, middleware, idempotency keys, and SSE streaming built-in.
Need Kotlin, Swift, or Python? Build a custom emitter as a WASM plugin with the specforge plugin SDK.
Every generated SDK is a complete, production-ready client — not just types. All capabilities available in TypeScript, Go, and Rust.
isX() / narrowX() helpers to safely narrow union types at runtime.fetch, http.Client, or reqwest::Client for testing.Idempotency-Key on POST/PUT/PATCH/DELETE — one key per retry loop, safe to replay.Logger interface with ConsoleLogger and NoopLogger defaults.--locale flag at generate time.Each generated SDK is a standalone project — no shared runtime dependency, no version coupling.
import { createClient, bearerAuth, streamSse } from "./sdk/src/index.ts"; const client = createClient({ baseUrl: "https://api.example.com", auth: bearerAuth(() => process.env.API_TOKEN!), maxConcurrent: 8, dedupe: true, idempotency: true, retry: { maxRetries: 3 }, }); // Typed request & response — no `any` const page = await client.pets.listPets({ limit: 20 }); // SSE streaming with proper error handling const res = await client.request("GET", "/events"); for await (const ev of streamSse(res)) { console.log(ev.event, ev.data); }
Dual ESM/CJS package, native fetch — zero runtime dependencies. Discriminated union error types, isX()/narrowX() type guards for oneOf, per-model validate functions.
package main import ( "context" "fmt" "log" "os" "time" sdk "github.com/acme/widget-go" ) func main() { c := sdk.NewClient(). WithBaseURL("https://api.example.com"). WithBearerToken(os.Getenv("API_TOKEN")). WithTimeout(10 * time.Second). WithMaxConcurrent(8). WithDedupe(true). WithIdempotency(true). WithRetry(sdk.DefaultRetryOptions()) // Middleware for logging & tracing c.Use(func(ctx context.Context, req *sdk.MiddlewareRequest, next func(context.Context, *sdk.MiddlewareRequest) (*sdk.MiddlewareResponse, error), ) (*sdk.MiddlewareResponse, error) { start := time.Now() res, err := next(ctx, req) log.Printf("%s %s %v", req.Method, req.URL, time.Since(start)) return res, err }) pets, err := c.ListPets(context.Background(), 20) if err != nil { log.Fatal(err) } fmt.Println(pets) }
Stdlib only (net/http, encoding/json) — zero third-party dependencies. Embedded structs for allOf, New{Union} helpers for discriminated oneOf deserialization.
use std::time::Duration; use widget_sdk::{api, Client}; use widget_sdk::streaming::SseStream; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = Client::builder() .base_url("https://api.example.com") .bearer_token(std::env::var("API_TOKEN")?) .timeout(Duration::from_secs(10)) .max_concurrent(8) .dedupe(true) .idempotency(true) .build()?; // Fully typed request/response let pets = api::list_pets(&client, Some(20)).await?; // SSE streaming let res = client .request_stream(reqwest::Method::GET, "/events", &[], None) .await?; let mut sse = SseStream::new(res.bytes_stream()); while let Some(ev) = sse.next_event().await? { println!("{}: {}", ev.event, ev.data); } Ok(()) }
reqwest + serde + tokio async. #[serde(flatten)] for allOf, discriminant() for oneOf, SseStream for SSE parsing over bytes_stream().
One binary, 27+ commands. Cross-compiled for Linux, macOS, and Windows. Use specforge <cmd> --help for full flags.
Pick your platform. Run one command. You'll have specforge generating SDKs in under a minute.
# Download the latest Linux release (~7 MB) curl -sSL https://github.com/amafjarkasi/specforge-openapi-sdk-codegen/releases/latest/download/specforge-linux-amd64 -o specforge # Make it executable and install chmod +x specforge sudo mv specforge /usr/local/bin/ # For ARM64 (Raspberry Pi, Graviton, etc.): # download specforge-linux-arm64 instead # ✓ specforge is now on your $PATH
# Apple Silicon (M1/M2/M3) curl -sSL https://github.com/amafjarkasi/specforge-openapi-sdk-codegen/releases/latest/download/specforge-darwin-arm64 -o specforge chmod +x specforge sudo mv specforge /usr/local/bin/ # Intel Macs: # download specforge-darwin-amd64 instead # macOS may quarantine the binary — strip it first: xattr -d com.apple.quarantine /usr/local/bin/specforge # ✓ specforge is now on your $PATH
# PowerShell — Windows 10/11 (x64) Invoke-WebRequest -Uri "https://github.com/amafjarkasi/specforge-openapi-sdk-codegen/releases/latest/download/specforge-windows-amd64.exe" -OutFile "specforge.exe" # Move it somewhere on your $env:PATH Move-Item specforge.exe C:\Windows\System32\ # Or use Scoop: scoop install specforge # ✓ specforge is now on your PATH
# Requires Rust 1.75+ — get it at rustup.rs git clone https://github.com/amafjarkasi/specforge-openapi-sdk-codegen.git cd specforge-openapi-sdk-codegen # Build and install the CLI (~2 min, cached after) cargo install --path crates/specforge-cli --locked # ✓ specforge is now on your $PATH
Build the CLI and generate your first SDK in under 2 minutes.
# 1. Build git clone https://github.com/amafjarkasi/specforge-openapi-sdk-codegen cd specforge-openapi-sdk-codegen cargo build -p specforge-cli # 2. Generate a TypeScript SDK ./target/debug/specforge generate fixtures/petstore.yaml -l ts -o ./sdk-ts # 3. Lint a spec ./target/debug/specforge check fixtures/petstore.yaml # 4. Check for breaking changes ./target/debug/specforge diff openapi-v1.yaml openapi-v2.yaml
specforge handles real-world specs from the world's largest APIs — no toy examples required.
Parallel IR resolution, zero JVM overhead. Rust-native speed.
Generated TS SDKs use native fetch only. No axios, no node-fetch, no surprises.
oneOf, allOf, anyOf, discriminator — all resolved correctly. No any leaks.
Same IR powers TypeScript, Go, Rust, and WASM emitters. Consistency guaranteed.
Full IDE integration with 23+ commands. Generate, validate, diff, and analyze specs without leaving your editor.
Pick your language and output directory. SDK generated in seconds.
Configurable spec validation every time you save. Catch errors early.
Ctrl+Shift+G to generate, Ctrl+Shift+K to check. Fast and ergonomic.
Right-click any spec file for quick access to all commands.
Quick access to mock server and spec status at a glance.
Real-time feedback for long-running operations.
18 curated specs from the world's most popular APIs. Generate SDKs instantly with real-world schemas.
Plus 10 more: Atlassian, Bitbucket, Vercel, Netlify, CircleCI, Slack, Discord, Linear, Jira, and Confluence.
specforge market list
The specforge CLI is MIT licensed — use it, fork it, ship it, no strings. Optional paid plans add commercial warranty, private registry, hosted docs, and team features.
Cloud Pro launching Q1 2027. Join the waitlist — first 100 teams get founding member pricing locked for life.