Semantic OpenAPI generator for Go projects.
GoSemRoute focuses on semantic recognition, not only annotation parsing.
It walks routing code, follows helper wrappers, and infers request/response schemas from real code paths.
- Supported:
-
echov5 / v4 -
gin
-
- Planned:
-
fiber -
chi
-
This tool is optimized for real internal codebases, but it is still static analysis.
If you hit unsupported patterns, open an issue with a minimal code sample.
Use the gswr web command in the project root directory to start an embedded API reference UI.
Install CLI:
go install github.com/arsfy/gswr/cmd/gswr@latestRun:
Generate YAML:
gswr generate
# short alias
gswr ggswr automatically discovers package main / func main() and, when a
project has multiple binaries, selects the only entry that produces API routes.
You can also scan another project directory or explicitly resolve ambiguity:
gswr g ../my-service
gswr g --entry ./cmd/control-api/main.goGenerate JSON:
gswr g --out docs/openapi.jsonForce format explicitly:
gswr --entry ./main.go --out docs/openapi.out --format json
gswr --entry ./main.go --out docs/openapi.out --format yamlOpen the embedded API reference UI:
gswr web
# scan another project
gswr web ../my-service
# custom bind address and starting port
gswr web --host 0.0.0.0 --port 45000The command starts at http://127.0.0.1:43877 and opens it in the default
browser.
Upgrade a CLI installed with go install:
gswr upgradeThe command checks the latest GitHub Release and installs that concrete version. Locally built or manually downloaded binaries are left untouched and must be updated manually from GitHub Releases.
Most generators rely heavily on doc comments.
GoSemRoute additionally infers API shape from code semantics, so it can still produce useful docs with partial or missing annotations.
- Route discovery with nested
Group(...)recursion and cross-file router chaining - Input inference from
Param,QueryParam,QueryParamOr,FormValue,FormValueOr Bind(&req)inference viaparam/query/header/jsontags and required constraints- Field visibility via the
openapistruct tag (-,readOnly,writeOnly) - OpenAPI 3.1 schemas with nullable pointer types, UUID format inference, and base64
[]byteencoding - Response inference from direct
c.JSON(...)returns and helper wrappers likeresp.Success(...) - Multi-exit response collection (
returnin different branches) - Type inference across nested structs, map literals, and helper argument binding
- Authentication inference from middleware semantics (
bearer,cookie,headerapiKey) - Tag support via explicit
@Tags/@tagand automatic path-based fallback grouping
- Operation:
@summary/@Summary,@description/@Description,@tag/@tags/@Tags - Main metadata:
@title,@version,@description,@BasePath/@basepath,@host,@schemes
Struct fields can be hidden from or annotated in the generated schema with an
openapi struct tag. It is independent of encoding/json, so adding it never
changes runtime (de)serialization.
| Tag | Effect |
|---|---|
openapi:"-" |
Omit the field from request, response and component schemas. Use this for internal config that is exchanged between the control plane and edge nodes but is not part of the public API. |
openapi:"readOnly" |
Emit the field with the native OpenAPI readOnly: true flag (present in responses, ignored on writes). |
openapi:"writeOnly" |
Emit the field with the native OpenAPI writeOnly: true flag (present in requests, ignored on reads). |
type WAFPolicy struct {
Enabled bool `json:"enabled"`
GeoIPDatabase string `json:"geoip_database,omitempty" openapi:"-"` // control-plane internal
}readOnly/writeOnly are kept as native Schema Object flags instead of
stripping the field at the request/response stage: gswr caches one component
per package+type, and the same type is typically shared between request and
response bodies. Removing the field would corrupt the shared component, whereas
readOnly/writeOnly annotate it without conflict.
Generated documents use OpenAPI 3.1.0 and its JSON Schema vocabulary. Go
pointer fields are nullable, []byte is represented as a base64-encoded string,
and UUID types from common UUID packages use format: uuid.
| Go type | OpenAPI 3.1 schema |
|---|---|
*string |
type: [string, "null"] |
*MyStruct |
anyOf: [$ref, {type: "null"}] |
[]byte / []uint8 |
type: string, contentEncoding: base64 |
uuid.UUID |
type: string, format: uuid |
GoSemRoute can infer response schema through helper layers:
func Success(c *echo.Context, data any) error {
return c.JSON(http.StatusOK, types.Response{Code: "ok", Data: data})
}
func List(c *echo.Context) error {
id, _ := ParseIDParam(c, "id")
return Success(c, map[string]any{
"id": id,
})
}Generated 200 schema will include a typed data.id field instead of a generic object.
package
respis a secondary abstraction layer for input and output handling.
// @summary Edit user
// @description Edits user profile fields with helper-based parsing.
// @Tags user
func edit(c *echo.Context) error {
id, _ := resp.ParseIDParam(c, "id")
age := resp.ParseIntForm(c, "age", 18)
email := c.FormValueOr("email", "default@example.com") // Description π
if id <= 0 {
return resp.BadRequest(c, "id <= 0")
}
return resp.Success(c, map[string]any{
"id": id,
"age": age,
"email": []string{
email,
},
}) // Response Description π
}/api/v1/user/{id}:
post:
operationId: edit
summary: Edit user
description: Edits user profile fields with helper-based parsing.
tags:
- user
security:
- header_Authorization: []
x-middlewares:
- AuthMiddleware
parameters:
- name: id
in: path
required: true
schema:
type: number
- name: age
in: query
schema:
type: number
- name: email
in: query
description: "Description π"
schema:
type: string
responses:
"200":
description: "Response Description π"
content:
application/json:
schema:
type: object
properties:
code:
type: string
enum:
- ok
data:
type: object
properties:
age:
type: number
email:
type: array
items:
type: string
id:
type: number
required:
- age
- email
- id
"400":
description: Client Error
content:
application/json:
schema:
type: object
properties:
code:
type: string
enum:
- id <= 0- Dynamic runtime-only patterns (reflection-heavy dispatch, generated handlers) may not be fully resolved
- Ambiguous symbols with no import/type context may degrade to generic object schema
- This is static analysis, not runtime tracing
Run tests:
go test ./...Rebuild the frontend assets embedded by the CLI:
cd web
pnpm build