Design document for OpenAPI SDK generation feature: - Hybrid generators (native + openapi-generator fallback) - Core 4 languages: TypeScript, Python, Go, PHP - Auto-detection: config → common paths → Laravel Scramble - Breaking change detection with oasdiff - Monorepo publish support Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.4 KiB
7.4 KiB
SDK Generation Design
Summary
Generate typed API clients from OpenAPI specs for TypeScript, Python, Go, and PHP. Includes breaking change detection via semantic diff.
Design Decisions
- Generator approach: Hybrid - native generators where available, openapi-generator fallback
- Languages: TypeScript, Python, Go, PHP (Core 4)
- Detection: Config → common paths → Laravel Scramble
- Output: Local
sdk/+ optional monorepo publish - Diff: Semantic with oasdiff, CI-friendly exit codes
- Priority: DX (developer experience)
Package Structure
pkg/sdk/
├── sdk.go # Main SDK type, orchestration
├── detect.go # OpenAPI spec detection
├── diff.go # Breaking change detection (oasdiff)
├── generators/
│ ├── generator.go # Generator interface
│ ├── typescript.go # openapi-typescript-codegen
│ ├── python.go # openapi-python-client
│ ├── go.go # oapi-codegen
│ └── php.go # openapi-generator (Docker)
└── templates/ # Package scaffolding templates
├── typescript/
│ └── package.json.tmpl
├── python/
│ └── setup.py.tmpl
├── go/
│ └── go.mod.tmpl
└── php/
└── composer.json.tmpl
OpenAPI Detection Flow
1. Check config: sdk.spec in .core/release.yaml
↓ not found
2. Check common paths:
- api/openapi.yaml
- api/openapi.json
- openapi.yaml
- openapi.json
- docs/api.yaml
- swagger.yaml
↓ not found
3. Laravel Scramble detection:
- Check for scramble/scramble in composer.json
- Run: php artisan scramble:export --path=api/openapi.json
- Use generated spec
↓ not found
4. Error: No OpenAPI spec found
Generator Interface
type Generator interface {
// Language returns the generator's target language
Language() string
// Generate creates SDK from OpenAPI spec
Generate(ctx context.Context, opts GenerateOptions) error
// Available checks if generator dependencies are installed
Available() bool
// Install provides installation instructions
Install() string
}
type GenerateOptions struct {
SpecPath string // OpenAPI spec file
OutputDir string // Where to write SDK
PackageName string // Package/module name
Version string // SDK version
}
Native Generators
| Language | Tool | Install |
|---|---|---|
| TypeScript | openapi-typescript-codegen | npm i -g openapi-typescript-codegen |
| Python | openapi-python-client | pip install openapi-python-client |
| Go | oapi-codegen | go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest |
| PHP | openapi-generator (Docker) | Requires Docker |
Fallback Strategy
func (g *TypeScriptGenerator) Generate(ctx context.Context, opts GenerateOptions) error {
if g.Available() {
return g.generateNative(ctx, opts)
}
return g.generateDocker(ctx, opts) // openapi-generator in Docker
}
Breaking Change Detection
Using oasdiff for semantic OpenAPI comparison:
import "github.com/tufin/oasdiff/diff"
import "github.com/tufin/oasdiff/checker"
func (s *SDK) Diff(base, revision string) (*DiffResult, error) {
// Load specs
baseSpec, _ := load.From(loader, base)
revSpec, _ := load.From(loader, revision)
// Compute diff
d, _ := diff.Get(diff.NewConfig(), baseSpec, revSpec)
// Check for breaking changes
breaks := checker.CheckBackwardCompatibility(
checker.GetDefaultChecks(),
d,
baseSpec,
revSpec,
)
return &DiffResult{
Breaking: len(breaks) > 0,
Changes: breaks,
Summary: formatSummary(d),
}, nil
}
Exit Codes for CI
| Exit Code | Meaning |
|---|---|
| 0 | No breaking changes |
| 1 | Breaking changes detected |
| 2 | Error (invalid spec, etc.) |
Breaking Change Categories
- Removed endpoints
- Changed required parameters
- Modified response schemas
- Changed authentication requirements
CLI Commands
# Generate SDKs from OpenAPI spec
core sdk generate # Uses .core/release.yaml config
core sdk generate --spec api.yaml # Explicit spec file
core sdk generate --lang typescript # Single language
# Check for breaking changes
core sdk diff # Compare current vs last release
core sdk diff --spec api.yaml --base v1.0.0
# Validate spec before generation
core sdk validate
core sdk validate --spec api.yaml
Config Schema
In .core/release.yaml:
sdk:
# OpenAPI spec source (auto-detected if omitted)
spec: api/openapi.yaml
# Languages to generate
languages:
- typescript
- python
- go
- php
# Output directory (default: sdk/)
output: sdk/
# Package naming
package:
name: myapi # Base name
version: "{{.Version}}"
# Breaking change detection
diff:
enabled: true
fail_on_breaking: true # CI fails on breaking changes
# Optional: publish to monorepo
publish:
repo: myorg/sdks
path: packages/myapi
Output Structure
Each generator outputs to sdk/{lang}/:
sdk/
├── typescript/
│ ├── package.json
│ ├── src/
│ │ ├── index.ts
│ │ ├── client.ts
│ │ └── models/
│ └── tsconfig.json
├── python/
│ ├── setup.py
│ ├── myapi/
│ │ ├── __init__.py
│ │ ├── client.py
│ │ └── models/
│ └── requirements.txt
├── go/
│ ├── go.mod
│ ├── client.go
│ └── models.go
└── php/
├── composer.json
├── src/
│ ├── Client.php
│ └── Models/
└── README.md
Publishing Workflow
SDK publishing integrates with the existing release pipeline:
core release
→ build artifacts
→ generate SDKs (if sdk: configured)
→ run diff check (warns or fails on breaking)
→ publish to GitHub release
→ publish SDKs (optional)
Monorepo Publishing
For projects using a shared SDK monorepo:
- Clone target repo (shallow)
- Update
packages/{name}/{lang}/ - Commit with version tag
- Push (triggers downstream CI)
The SDK tarball is also attached to GitHub releases for direct download.
Implementation Steps
- Create
pkg/sdk/package structure - Implement OpenAPI detection (
detect.go) - Define Generator interface (
generators/generator.go) - Implement TypeScript generator (native + fallback)
- Implement Python generator (native + fallback)
- Implement Go generator (native)
- Implement PHP generator (Docker-based)
- Add package templates (
templates/) - Implement diff with oasdiff (
diff.go) - Add CLI commands (
cmd/core/sdk.go) - Integrate with release pipeline
- Add monorepo publish support
Dependencies
// go.mod additions
require (
github.com/tufin/oasdiff v1.x.x
github.com/getkin/kin-openapi v0.x.x
)
Testing
- Unit tests for each generator
- Integration tests with sample OpenAPI specs
- Diff tests with known breaking/non-breaking changes
- E2E test generating SDKs for a real API