Changelog
v1.3.0
A spec-correctness release. Fixes the five bugs that made the generated openapi.json wrong or unusable for code generators (router-style paths, dropped embedded structs, silently overwritten duplicates, missing null for pointers, missing operationId), maps go-playground/validator rules onto JSON Schema constraints, and hardens the docs UI. Named structs are now emitted as reusable components.schemas entries, and packages that were never used by the runtime have been removed.
go get github.com/gopackx/open-swag-go@v1.3.0Breaking changes
- Dead packages removed. The following were never wired into the runtime: nothing in the handler, the spec builder or the docs page called them, so their settings had no effect. Code that imports them no longer compiles.
pkg/auth(scheme constructors, playground config, credential persistence). Useopenswag.BearerAuth,APIKeyAuth,APIKeyQueryAuth,BasicAuth,CookieAuthinConfig.Auth.Schemesand theSecurity*constants from the root package.pkg/tryit(console config, environments, request history) andpkg/tryit/snippets(code snippet generators). The try-it console, request history, environments and code snippets in the docs UI are Scalar features and need no configuration.pkg/ui/themes.go:Theme,ThemeColors,ThemeFonts,PredefinedThemes,GetTheme,Theme.ToCSS.UIConfig.Themeis passed to Scalar as-is.pkg/examples/templates.goandpkg/examples/faker.go:Template,TemplateRegistry,NewTemplateRegistry,Faker,NewFaker, and theexamples.Config.UseFakerfield.examples.New,Generate,GenerateJSONandDefaultTypeExamplesremain.pkg/schema/validator.go(Validator,NewValidator,ValidationError) andpkg/schema/types.go(TypeMappings,FormatMappings,GetTypeMapping).
- Deprecated
UIConfigfields.SidebarSearch,TagGroupingandCollapsibleSchemasare kept for compatibility but markedDeprecated: has no effect. They were never passed to Scalar, which always shows the sidebar search, groups operations by tag and renders collapsible model sections.AuthConfig.PersistCredentialsis likewise not read anywhere and has no runtime effect.
Behaviour changes in generated spec
-
Named structs are
$refcomponents. Every named struct used in a request or response body (directly, or nested via a field, slice element or map value) is emitted once undercomponents.schemasand referenced with{"$ref": "#/components/schemas/<Name>"}, so code generators produce named types (components['schemas']['User']with openapi-typescript). Anonymous structs, embedded structs (still flattened into the parent) and all parameters stay inline. Recursive structs get a real self-$refinstead of theRecursive reference to ...placeholder.- Names are the Go type name. When two different types share a name, the first registered keeps it (endpoint order; request body, then responses by ascending status code, then field order) and the next gets its package name as a prefix (
model.User→modelUser), then a numeric suffix (modelUser2). Generic names are sanitised to[A-Za-z0-9_.-]:Page[github.com/x/dto.User]→Page_dto.User. - Nullable references. A pointer to a named struct is
{"oneOf": [{"$ref": "#/components/schemas/Address"}, {"type": "null"}]}. A fielddescriptionis kept as a sibling of the$ref(on the outer object for a nullable one); other tags on a struct-typed field (example,enum,format,validatebounds) are dropped. Config.InlineSchemas: truerestores the previous output. See Schema Components.
"requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUserRequest" } } } } - Names are the Go type name. When two different types share a name, the first registered keeps it (endpoint order; request body, then responses by ascending status code, then field order) and the next gets its package name as a prefix (
-
Nullable fields use OpenAPI 3.1 type arrays. Pointer fields (
Name *string) now produce"type": ["string", "null"]. Previously they were emitted as a plainstring. The 3.0nullablekeyword is never written. A top-level pointer passed toRequestBody.Schemaor a response (&User{}) is not nullable, and query/path parameters treat a pointer as optional rather than nullable. -
Every operation has an
operationId. WhenEndpoint.OperationIDis empty one is derived from the method and path in lowerCamelCase:GET /users/{id}becomesgetUsersById,POST /usersbecomespostUsers,GET /becomesgetRoot. -
Router-style paths are normalised.
/users/:id,/files/*pathand chi regex segments such as/users/{id:[0-9]+}are written to the spec as/users/{id},/files/{path}and/users/{id}. -
Embedded structs with an unexported type are promoted.
type base struct{...}embedded asbaseused to vanish from the schema; its exported fields are now flattened into the parent likeencoding/jsondoes. A field declared on the outer struct shadows a promoted one with the same name, and a json tag on the embedded field (base `json:"base"`) makes it a nested property instead. -
required_ifand other conditional rules no longer mark a field required. Only a barerequiredrule (in theswagger,validateorbindingtag) does. -
HEAD,OPTIONSandTRACEare supported. They used to produce an empty path item ("/health": {}).
New public API
func (d *Docs) Validate() errorreports duplicate method+path pairs, unsupported HTTP methods and duplicateoperationIds in one joined error. Call it once afterAddAll.Endpoint.OperationID stringoverrides the generatedoperationId.UIConfig.ScalarVersion stringpins the@scalar/api-referenceversion loaded from the CDN. Empty usesui.DefaultScalarVersion("1.75.0").UIConfig.SpecCORSOrigin stringcontrols theAccess-Control-Allow-Originheader on the spec endpoint:""keeps*,"-"omits the header, any other value is sent as-is.enum:"a,b,c"struct tag sets explicit enum values, converted to the field type. It takes precedence overvalidate:"oneof=...".- Validator rule mapping.
validateandbindingtags now produce constraints:oneofbecomesenum,min/max/gte/lte/gt/lt/lenbecomeminLength/maxLength,minimum/maximumorminItems/maxItemsdepending on the field type,email/uuid/url/ipv4/ipv6/hostnamesetformat, anddiveapplies the following rules to slice items. See Schema Generation. Config.InlineSchemas bool(defaultfalse) disables schema components: nocomponents.schemas, every struct inlined at each use, recursive back-edges become{type: object, description: "Recursive reference to ..."}.- Schema registry in
pkg/schema:Registry(Names(),Schema(name),Len(),NameOf(reflect.Type)),NewRegistry(),FromTypeWithRegistry(v, reg),FromReflectTypeWithRegistry(t, reg)and the constantRefPrefix = "#/components/schemas/".FromTypeandFromReflectTypeare unchanged and still inline everything. See Schema Package. - Query and path parameter structs use the body tag rules.
swagger:"required",description,format,example,enumand validator rules now work onEndpoint.QueryParams/Endpoint.PathParamsfields too.
Fixed
- fix(openapi): A second endpoint with the same method and path no longer silently replaces the first without any signal.
BuildSpecstill keeps the last one, butValidatereports it. - fix(openapi): Unsupported HTTP methods no longer produce an empty path item; they are skipped and reported by
Validate. - fix(openapi):
Parameter.Exampleon an explicitly declaredEndpoint.Parametersentry is now written to the spec; previously only struct-derived parameters could carry an example. - fix(ui): The docs HTML is rendered with
html/template, soInfo.Titleand the spec URL are escaped.CustomCSSis still inserted verbatim and must come from trusted configuration. - fix(ui): The Scalar CDN script is pinned to a version instead of tracking the latest upstream release.
- fix(handler):
DocsAuthwithEnabled: truebut noAPIKeyand noUsername+Passwordnow answers every request with HTTP 500DocsAuth enabled but no credentials configuredinstead of a 401 that looked like a wrong password. - fix(versioning): The version differ understands 3.1 type arrays. Adding
nullto a field's type is not reported as a type change. - fix(versioning): The differ resolves
#/components/schemas/<Name>references (following up to 8 chained refs) when comparing request-body required fields and property types, and treats a nullableoneOf [$ref, null]like the referenced type. Moving a struct into components, or making it nullable, is not reported as a type change. A reference that cannot be resolved is compared as$ref:<ref>. - fix(schema): Recursive structs are described completely through a self-
$refinstead of being cut off at the back-edge. - docs: README and the bundled Claude Skill corrected (no
Handlerfield onEndpoint; the snippet package import shown in the README and its PHP generator did not exist). The try-it console, auth playground and code snippets in the docs UI are Scalar features. - ci: CI now runs on
masterwithgofmt,go vetandgolangci-lint.
Migrating from v1.2.0
- Regenerate your TypeScript types. Pointer fields now come out as
string | null(ornumber | null, …) instead ofstring, so frontend code may need null checks. Named structs are now named types (components['schemas']['User']with openapi-typescript, named interfaces with orval) instead of anonymous inline objects. - Remove imports of the deleted packages (
pkg/auth,pkg/tryit,pkg/tryit/snippets, and the removed parts ofpkg/ui,pkg/examplesandpkg/schemalisted under Breaking changes). Replaceauth.BearerAuth(...)and friends withopenswag.BearerAuth(name)etc. inConfig.Auth.Schemes; drop console, snippet and playground configuration. - Need the old inline spec for now? Set
InlineSchemas: trueonConfigto keep the pre-v1.3.0 output while you migrate generated clients. - Move tags off struct-typed fields.
example,enum,formatandvalidatebounds on a field whose type is a named struct are dropped next to the$ref; put them on the struct's own fields. - Check generated names. orval names functions and hooks after
operationId, and openapi-typescript keys itsoperationsinterface by it. Every operation now has one, so names your generator previously invented may change; setEndpoint.OperationIDwhere you need a specific name. - Call
docs.Validate()afterAddAlland treat an error as fatal. If it reports duplicates, one of your endpoints was silently missing from the spec before. - Review fields relying on
required_if(orrequired_with,required_without, …). They are no longer listed inrequired; addswagger:"required"if they really are always required.
See the Migration Guide for examples.
v1.2.0
A bug-fix + DX release. Closes the four bugs that produced wrong specs at runtime (self-referential structs, ignored ContentType, silent bearer fallback, dormant type-change detector) and finally ships the public API the README has been documenting all along.
go get github.com/gopackx/open-swag-go@v1.2.0Breaking change
- The
Responsestruct is renamed toResponseSpecsoopenswag.Response(desc, schema)can be the constructor the README documents. Field-keyed literals insideopenswag.Responses{ ... }keep compiling unchanged — only bareopenswag.Response{...}literals need to migrate (useopenswag.Response("desc", T{})oropenswag.ResponseSpec{...}).
Fixed
- fix(schema): Self-referential structs (e.g.
type Category struct { Children []Category }) no longer cause a stack overflow during spec generation.fromReflectTypenow threads a visiting-type set; back-edges return a bounded object schema with a description note. - fix(openapi):
RequestBody.ContentTypeis now honored end-to-end.multipart/form-dataandapplication/x-www-form-urlencodedbodies appear in the spec under the correct media type instead of being silently rewritten toapplication/json. Addedpkg/spec.RequestBody.WithContent(mediaType, schema)to back the new behavior. - fix(openapi): Custom security scheme names referenced from
Endpoint.Securityare no longer silently coerced intohttp-bearer. Unknown names are now omitted fromcomponents.securitySchemesso typos surface immediately. Register custom schemes explicitly via the newConfig.Auth.Schemesfield. - fix(versioning):
compareOperationsnow produces entries forBreakingTypeChanged. Changes to a request-body field's JSONtype/formatand changes to a parameter's schema type are flagged as breaking, with migration hints. Also fixed a latent index-out-of-range bug ingetMigrationGuide(short descriptions could panic the migration formatter).
New public API
The README has documented these helpers for several versions, but the package never exposed them — Quick Start examples could not compile. They exist now:
- Request body builders:
Body(schema),BodyWithDesc(desc, schema),FormBody(schema),FormURLEncodedBody(schema)— all return*RequestBodyready to drop intoEndpoint.RequestBody. - Response constructor:
Response(desc, schema...)returns aResponseSpec. Pass the schema as the second argument or omit it for empty responses (e.g. 204 No Content). The type aliasResponses = map[int]ResponseSpecexists for the decorator-style map literal. - Parameter helpers:
PathParam,QueryParam,RequiredQueryParam,HeaderParam,CookieParam— all return aParameterready forEndpoint.Parameters. - Auth scheme helpers:
BearerAuth(name),APIKeyAuth(name, header),APIKeyQueryAuth(name, param),BasicAuth(name),CookieAuth(name, cookieName)— all return anAuthSchemefor registration viaConfig.Auth.Schemes. - Config surface:
Config.Auth AuthConfig(withPersistCredentialsandSchemesfields) plus theAuthSchemestruct. The newSecurityCookieAuthconstant joins the existingSecurity*family for the predefined-name shortcut.
Migrating from v1.1.1
-
Replace bare
openswag.Response{...}literals withopenswag.Response("desc", Schema{})oropenswag.ResponseSpec{...}. Literals insideopenswag.Responses{ ... }(or the equivalent typed map) continue to compile via Go's composite-literal type inference. -
If you were referencing custom security scheme names from
Endpoint.Securityand relying on them magically becominghttp-bearer, register them explicitly:Auth: openswag.AuthConfig{ Schemes: []openswag.AuthScheme{ openswag.BearerAuth(openswag.SecurityBearerAuth), openswag.APIKeyAuth("apiKey", "X-API-Key"), }, }, -
File-upload endpoints that previously had a
ContentType: "multipart/form-data"set onRequestBodywill now produce a different spec than before — review your generatedopenapi.jsonand update downstream clients/code generators accordingly.
v1.1.1
Cleaner specs out of the box. Examples are now opt-in: you only see them where you ask for them, and null is supported as a first-class example value. Fully backwards-compatible — no API signatures changed.
go get github.com/gopackx/open-swag-go@v1.1.1Behavior change worth flagging
- Examples are now opt-in. Auto-generated example values for primitive fields have been removed. Previously every primitive field got a placeholder (
"string",0,false, …) in the spec even without anexampletag, which produced noisy Scalar UI. Now examples appear only where you setexample:"..."on the field.
What's new
example:"null"literal — Settingexample:"null"on any field type now produces actual JSONnullin the spec (previously only worked for slices/maps). Useful for documenting nullable fields.
Fixed
- Stray
"example": nullentries no longer leak into request body schemas when no example is provided (typed-nil interface bug).
Migrating from v1.1.0
If you relied on the old auto-generated placeholders to populate the Scalar UI, add explicit example:"..." tags to the fields you want shown — see the Migration Guide for details.
v1.1.0
All 12 fixes are backwards-compatible — no public API signatures changed, only additive struct fields and behavior corrections.
go get github.com/gopackx/open-swag-go@v1.1.0Critical Fixes
- fix(spec): Fixed port-to-string conversion in
CommonServers.Localhost()andLocalhostServer()— replaced brokenstring(rune(port))withstrconv.Itoa(port). Also fixed the same pattern inintToString()inopenapi.go. - fix(schema): Example tag values are now coerced to the correct type via
ConvertExampleToType().example:"42"on anint64field now produces the integer42, not the string"42". Applied inpkg/schema/tags.go,openapi.go, andpkg/examples/generator.go. - fix(schema):
$refpointers are no longer silently dropped during schema conversion — addedRef: s.Refmapping inconvertSchema().
High Severity Fixes
- fix(schema): Embedded (anonymous) struct fields are now detected via
field.Anonymousand their properties are promoted into the parent schema. - fix(schema):
map[K]Vtypes now correctly generate{"type":"object","additionalProperties":{...}}. AddedAdditionalPropertiesfield toschema.Schemaand conversion inconvertSchema(). - fix(schema):
interface{}/anyfields now produce an empty schema{}(accepts any type) instead of{"type":"object"}. Validator updated to accept empty schemas.
Medium Severity Fixes
- fix(schema): Fields with
json:",omitempty"are no longer marked as required even ifvalidate:"required"is present. - feat(schema): Added
Nullable,ReadOnly,WriteOnly,Deprecated,MinItems,MaxItems,UniqueItems,AllOf,OneOf,AnyOffields toschema.Schemaand full conversion support inconvertSchema(). - fix(schema): Added special handling for
time.Duration→{format:"duration"},[]byte→{format:"byte"},uuid.UUID→{format:"uuid"}. - fix(schema):
json:",omitempty"(empty name part) now correctly falls back to the Go field name.
Low Severity Fixes
- fix(openapi):
buildParamsFromStructnow prefers location-specific tags (querytag for query params,pathtag for path params) before falling back to generic tags. - feat(openapi): Request body schemas now include an auto-generated
examplefield built from struct field tags and type defaults.
v1.0.0
Initial release of open-swag-go.
- 5 framework adapters: Chi, Gin, Echo, Fiber, net/http
- Authentication playground with Bearer, Basic, API Key, Cookie, and OAuth2 support
- Try-it console with environment management and request history
- Auto-generated code snippets in curl, JavaScript, Go, and Python
- Version diffing with breaking change detection and migration guides
- Schema generation from Go structs using standard tags
- Theming with Scalar UI (purple, blue, green, light themes + dark mode)
- Docs page authentication with basic auth or API key
- Smart example generation from struct field names and tags