{"id":47754468,"url":"https://github.com/antst/go-apispec","last_synced_at":"2026-04-28T14:01:12.245Z","repository":{"id":348834008,"uuid":"1200047857","full_name":"antst/go-apispec","owner":"antst","description":"Generate OpenAPI 3.1 specs from Go source code via static analysis — zero annotations, automatic framework detection","archived":false,"fork":false,"pushed_at":"2026-04-03T09:27:09.000Z","size":31742,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T11:28:53.036Z","etag":null,"topics":["chi","code-generation","echo","fiber","gin","go","golang","gorilla-mux","openapi","openapi-generator","openapi3","static-analysis","swagger"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-03T01:32:36.000Z","updated_at":"2026-04-03T09:48:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/antst/go-apispec","commit_stats":null,"previous_names":["antst/go-apispec"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/antst/go-apispec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antst%2Fgo-apispec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antst%2Fgo-apispec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antst%2Fgo-apispec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antst%2Fgo-apispec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antst","download_url":"https://codeload.github.com/antst/go-apispec/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antst%2Fgo-apispec/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31554109,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chi","code-generation","echo","fiber","gin","go","golang","gorilla-mux","openapi","openapi-generator","openapi3","static-analysis","swagger"],"created_at":"2026-04-03T04:02:49.027Z","updated_at":"2026-04-28T14:01:12.214Z","avatar_url":"https://github.com/antst.png","language":"Go","readme":"# go-apispec: Generate OpenAPI from Go code\n\n[![CI](https://github.com/antst/go-apispec/actions/workflows/ci.yml/badge.svg)](https://github.com/antst/go-apispec/actions/workflows/ci.yml)\n[![Release](https://github.com/antst/go-apispec/actions/workflows/release.yml/badge.svg)](https://github.com/antst/go-apispec/actions/workflows/release.yml)\n![Coverage](https://img.shields.io/badge/coverage-95.7%25-brightgreen.svg)\n[![Go Version](https://img.shields.io/badge/go-1.25+-00ADD8?style=flat\u0026logo=go)](https://go.dev/)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/antst/go-apispec/blob/main/LICENSE)\n[![Go Reference](https://pkg.go.dev/badge/github.com/antst/go-apispec.svg)](https://pkg.go.dev/github.com/antst/go-apispec)\n\n**go-apispec** analyzes your Go source code and generates an OpenAPI 3.1 spec (YAML or JSON). Point it at your module — it detects the framework, follows the call graph from routes to handlers, and infers request/response types from real code.\n\n## Quick Start\n\n```bash\n# Install\ngo install github.com/antst/go-apispec/cmd/apispec@latest\n\n# Generate (auto-detects framework)\napispec --dir ./your-project --output openapi.yaml\n```\n\nThat's it. The tool detects your framework, finds all routes, resolves handler types, and writes the spec.\n\n## Features\n\n### Framework Support\n\n| Framework | Routes | Params | Request Body | Responses | Mounting/Groups |\n|-----------|--------|--------|-------------|-----------|-----------------|\n| **Chi** | Full | `chi.URLParam`, `r.FormValue`, `r.FormFile`, render pkg | `json.Decode`, `render.DecodeJSON` | `json.Encode`, `render.JSON`, `w.Write` | `Mount`, `Group` |\n| **Gin** | Full | `c.Param`, `c.Query` | `ShouldBindJSON`, `BindJSON` | `c.JSON`, `c.String`, `c.Data` | `Group` |\n| **Echo** | Full | `c.Param`, `c.QueryParam`, `r.FormValue`, `r.FormFile` | `c.Bind` | `c.JSON`, `c.String`, `c.Blob` | `Group` |\n| **Fiber** | Full | `c.Params`, `c.Query`, `c.FormValue`, `c.FormFile` | `c.BodyParser` | `c.JSON`, `c.Status().JSON` | `Mount`, `Group` |\n| **Gorilla Mux** | Full | Path template `{id}` | `json.Decode` | `json.Encode`, `w.Write` | `PathPrefix`, `Subrouter` |\n| **net/http** | Basic | Path template, `r.FormValue`, `r.FormFile` | `json.Decode` | `json.Encode`, `w.Write`, `http.Error` | Nested `ServeMux` |\n\nProjects using **multiple frameworks** simultaneously are fully supported — all routes from all detected frameworks appear in the spec.\n\nAll frameworks also detect `fmt.Fprintf`, `io.Copy`, and `io.WriteString` as response writes.\n\n### Analysis Capabilities\n\n**Response Detection**\n- Content-Type inference from `w.Header().Set(\"Content-Type\", \"image/png\")`\n- Dynamic content-type fallback: `w.Header().Set(\"Content-Type\", doc.MimeType)` → `application/octet-stream` (variable MIME types don't leak Go field paths into the spec)\n- `WriteHeader(201)` + `json.Encode(user)` merged into a single 201 response with schema\n- Error helper functions: `writeJSONError(w, http.StatusBadRequest, \"msg\")` → 400 response with ErrorResponse schema, traced through function parameters via ParamArgMap\n- Multiple calls to same helper: `writeJSONError(w, 400, ...)` + `writeJSONError(w, 404, ...)` → both status codes captured with correct schemas\n- Status code variable resolution: `status := http.StatusCreated; w.WriteHeader(status)` → 201\n- Cross-function status codes: `w.WriteHeader(getStatus())` where `getStatus()` returns a constant\n- Multiple response types for the same status code → `oneOf` schema\n- `[]byte` responses → `type: string, format: binary`\n- Bodyless status codes (1xx, 204, 304) never get body schemas (per RFC 7231)\n- Implicit 200 for handlers that write a body without explicit `WriteHeader`\n\n**Type Resolution**\n- Generic struct instantiation: `APIResponse[User]` → schema with `Data: $ref User`\n- Interface resolution: handlers registered via interface → concrete implementation schemas\n- `interface{}` parameter resolution: `respondJSON(w, 201, user)` where `data` is `interface{}` → resolves to concrete `User` type from the caller's argument\n- Conditional HTTP methods via CFG: `switch r.Method { case \"GET\": ... case \"POST\": ... }` → separate operations\n- Route path variables: `path := \"/users\"; r.GET(path, h)` → resolves variable to literal path\n- Decode receiver tracing: `json.NewDecoder(file).Decode(\u0026cfg)` not misclassified as request body\n- io.Copy source tracing: `io.Copy(w, strings.NewReader(...))` → `type: string`; `io.Copy(w, file)` → `format: binary`\n\n**Documentation Extraction**\n- Go doc comments on handler functions → OpenAPI `summary` and `description`\n- First sentence → `summary`, full comment → `description` (single-sentence comments don't duplicate)\n- No annotations needed — existing Go doc comments just work\n- Config overrides take precedence over doc comments\n\n**Schema Inference**\n- Required fields from `json:\",omitempty\"` absence and `binding:\"required\"` tags\n- Validator `dive` tag: `validate:\"dive,email\"` on `[]string` → items schema has `format: email`\n- `Mux.Vars()` map index expressions → path parameter names\n- Type mappings for `time.Time`, `uuid.UUID`, and custom types\n- Converter-typed params: `idStr := c.Param(\"id\"); strconv.Atoi(idStr)` → `integer`; `strconv.ParseBool` → `boolean`; `strconv.ParseFloat` → `number`; `uuid.Parse` → `string/uuid`. Works for both inline (`strconv.Atoi(r.FormValue(\"x\"))`) and var-bound (`if v := r.FormValue(\"x\"); v != \"\" { strconv.ParseBool(v) }`) idioms, including shadowed variables in separate `if`-init scopes\n- `r.FormFile(\"upload\")` → form parameter with `string`/`binary` schema\n- JSON-DTO field flow inference: when a decoded body's field is later passed to a converter (`uuid.Parse(body.SourceID)`, including pointer-deref `uuid.Parse(*body.TagsetID)`), apispec back-propagates the converter's schema (e.g. `format: uuid`) onto the struct field\n- Explicit per-field overrides via the `apispec:\"format=uuid,type=string\"` struct tag — covers fields the flow analysis can't reach (e.g. UUIDs that are read but never parsed in the handler, or `format: date-time` / `format: email` hints)\n- `requestBody.required: true` is emitted automatically when a handler reads the body via `json.Decode`, `json.Unmarshal`, `c.Bind`, `c.BodyParser`, etc.\n\n**Output Quality**\n- Deterministic YAML/JSON — sorted map keys, identical output across runs, safe for CI diffing\n- Short names by default — `DocumentHandler.GetContent` instead of `github.com/org/.../http.Deps.DocumentHandler.GetContent`\n- Config merging — `--config` extends auto-detected framework defaults instead of replacing them\n\n### Call Graph Visualization\n\nInteractive Cytoscape.js diagrams with:\n- Hierarchical tree layout with zoom, pan, and click-to-highlight\n- CFG branch coloring: green (if-then), red dashed (if-else), purple (switch-case)\n- Branch labels showing case values (e.g., \"GET\", \"POST\")\n- Paginated mode for large graphs (1000+ edges)\n- PNG/SVG export\n\n```bash\napispec --dir ./my-project --output openapi.yaml --diagram diagram.html\n```\n\n## Usage\n\n```bash\n# Basic generation\napispec --dir ./my-project --output openapi.yaml\n\n# With custom config\napispec --dir ./my-project --config apispec.yaml --output openapi.yaml\n\n# Legacy naming (fully-qualified operationIds and schema names)\napispec --dir ./my-project --output openapi.yaml --short-names=false\n\n# With call graph diagram\napispec --dir ./my-project --output openapi.yaml --diagram diagram.html\n\n# Skip CGO packages\napispec --dir ./my-project --output openapi.yaml --skip-cgo\n\n# Tune limits for large codebases\napispec --dir ./my-project --output openapi.yaml --max-nodes 100000 --max-recursion-depth 15\n\n# Performance profiling\napispec --dir ./my-project --output openapi.yaml --cpu-profile --mem-profile\n```\n\n### Key Flags\n\n| Flag | Short | Default | Description |\n|------|-------|---------|-------------|\n| `--output` | `-o` | `openapi.json` | Output file (`.yaml`/`.json`) |\n| `--dir` | `-d` | `.` | Project directory |\n| `--config` | `-c` | — | Custom YAML config |\n| `--short-names` | — | `true` | Strip module paths from names |\n| `--diagram` | `-g` | — | Save call graph as HTML |\n| `--title` | `-t` | `Generated API` | API title |\n| `--api-version` | `-v` | `1.0.0` | API version |\n| `--skip-cgo` | — | `true` | Skip CGO packages |\n| `--max-nodes` | `-mn` | `50000` | Max call graph nodes |\n| `--max-recursion-depth` | `-mrd` | `10` | Max recursion depth |\n| `--verbose` | `-vb` | `false` | Verbose output |\n\nFull flag list: `apispec --help`\n\n## Programmatic Usage\n\n```go\nimport (\n    \"os\"\n    \"github.com/antst/go-apispec/generator\"\n    \"github.com/antst/go-apispec/spec\"\n    \"gopkg.in/yaml.v3\"\n)\n\nfunc main() {\n    cfg := spec.DefaultChiConfig() // or DefaultGinConfig, DefaultEchoConfig, etc.\n    gen := generator.NewGenerator(cfg)\n    openapi, err := gen.GenerateFromDirectory(\"./your-project\")\n    if err != nil { panic(err) }\n    data, _ := yaml.Marshal(openapi)\n    os.WriteFile(\"openapi.yaml\", data, 0644)\n}\n```\n\n## Configuration\n\nAuto-detection works for most projects. For custom behavior, create `apispec.yaml`:\n\n```yaml\ninfo:\n  title: My API\n  version: 2.0.0\n\nshortNames: true  # false for legacy fully-qualified names\n\nframework:\n  routePatterns:\n    - callRegex: ^(?i)(GET|POST|PUT|DELETE|PATCH)$\n      recvTypeRegex: ^github\\.com/gin-gonic/gin\\.\\*(Engine|RouterGroup)$\n      handlerArgIndex: 1\n      methodFromCall: true\n      pathFromArg: true\n      handlerFromArg: true\n\ntypeMapping:\n  - goType: time.Time\n    openapiType: { type: string, format: date-time }\n  - goType: uuid.UUID\n    openapiType: { type: string, format: uuid }\n\nexternalTypes:\n  - name: github.com/gin-gonic/gin.H\n    openapiType: { type: object, additionalProperties: true }\n```\n\nFull configuration examples for each framework: see the `Default*Config()` functions in `internal/spec/config.go` for the built-in patterns.\n\n## How It Works\n\n```\nGo source → Package loading \u0026 type-checking → Framework detection\n  → AST traversal → Call graph + CFG construction → Pattern matching\n  → OpenAPI mapping → YAML/JSON output\n```\n\n1. Loads and type-checks all Go packages in the module\n2. Detects web frameworks from imports (supports multiple frameworks simultaneously)\n3. Builds a call graph from router registrations to handlers\n4. Builds a control-flow graph (CFG) via `golang.org/x/tools/go/cfg` for branch analysis\n5. Matches route, request, response, and parameter patterns against the call graph\n6. Resolves conditional methods, generic types, and interface implementations using CFG and type analysis\n7. Maps Go types to OpenAPI schemas (structs, enums, aliases, generics, validators)\n8. Serializes with sorted keys for deterministic output\n\n## Known Limitations\n\nThese are inherent to static analysis — analyzing code without executing it:\n\n| Limitation | Example | What Happens |\n|-----------|---------|-------------|\n| **Reflection-based routing** | Routes registered via `reflect.Value.Call` | Not visible in static analysis |\n| **Computed paths** | `r.GET(\"/api/\" + version, handler)` | String concatenation not evaluated; only literal and variable-assigned paths resolved |\n| **Complex cross-function values** | `func compute() int { return a + b }; WriteHeader(compute())` | Only functions with a single constant return are resolved; computed values are not traced |\n\n## Interactive Diagram Server\n\n```bash\n# Build and start\ngo build -o apidiag ./cmd/apidiag\n./apidiag --dir ./my-project --port 8080\n# Open http://localhost:8080\n```\n\nProvides a web UI for exploring call graphs with filtering, pagination, and export. See [cmd/apidiag/README.md](cmd/apidiag/README.md).\n\n## Development\n\n```bash\n# Build\nmake build\n\n# Test\nmake test\n\n# Lint\nmake lint\n\n# Coverage\nmake coverage\n\n# Update golden files after intentional output changes\n# (generates to temp, shows diff, requires confirmation)\ngo test ./internal/engine/ -run TestUpdateGolden -v\n```\n\n### Project Structure\n\n```\ngo-apispec/\n├── cmd/apispec/          CLI entry point\n├── cmd/apidiag/          Interactive diagram server\n├── generator/            High-level generator API\n├── spec/                 Public types (re-exports from internal/spec)\n├── internal/\n│   ├── core/             Framework detection\n│   ├── engine/           Generation engine\n│   ├── metadata/         AST analysis and metadata extraction\n│   └── spec/             OpenAPI mapping, patterns, schemas\n├── pkg/patterns/         Gitignore-style pattern matching\n└── testdata/             Framework fixtures + golden files\n```\n\n### Golden File Tests\n\nEvery `testdata/` directory has `expected_openapi.json` (short names) and `expected_openapi_legacy.json` (fully-qualified). Golden files use relative paths only — no machine-specific absolute paths.\n\n**One code path**: `generateGoldenSpec()` is the single function used by both comparison and generation. Tests never overwrite golden files.\n\n```bash\n# Compare (runs in CI — fails on mismatch):\ngo test ./internal/engine/ -run TestGolden -v\n\n# Update after intentional changes (explicit, never automatic):\ngo test ./internal/engine/ -run TestUpdateGolden -v\n```\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). In short:\n\n1. Fork, branch, make changes\n2. Add tests (`make test`)\n3. Run lint (`make lint`)\n4. Open a PR\n\n## License\n\nApache License 2.0 — see [LICENSE](LICENSE).\n\nOriginally forked from [apispec](https://github.com/ehabterra/apispec) by Ehab Terra.\n","funding_links":[],"categories":["Generators"],"sub_categories":["Search and Analytic Databases"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantst%2Fgo-apispec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantst%2Fgo-apispec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantst%2Fgo-apispec/lists"}