Skip to content

Repository files navigation

Demo

Click here for Youtube Demo Link

go-swagger3

CI Go Version License

Generate OpenAPI Specification v3 from Go godoc @ annotations and struct tags.

OpenAPI 3-first, with swag-class DX: @Accept/@Produce, per-operation security, generics, composition, framework UI adapters, and go-swagger3 fmt.

Migrating from swaggo/swag? See docs/MIGRATION_FROM_SWAG.md.

AI agent skills

Copy these into Claude / Cursor projects so agents can annotate existing Go APIs and generate OpenAPI 3 specs with go-swagger3:

Agent Source Copy to
Claude Code agent-skills/claude/SKILL.md .claude/skills/go-swagger3-docs/SKILL.md
Cursor agent-skills/cursor/cursor_rules.mdc .cursor/rules/go-swagger3.mdc

Table of content

1. Install

go install github.com/parvez3019/go-swagger3@latest

2. Documentation Generation

Using binary

Go to the folder where is main.go in

// go.mod and main file are in the same directory
go-swagger3 --module-path . --output oas.json --schema-without-pkg --generate-yaml true

// go.mod and main file are in the different directory
go-swagger3 --module-path . --main-file-path ./cmd/xxx/main.go --output oas.json --schema-without-pkg --generate-yaml true

// in case you get 'command not found: go-swagger3' error, please export add GOPATH/bin to PATH
export PATH="$HOME/go/bin:$PATH"

Notes:
- `--schema-without-pkg` — schema names without package prefixes
- `--generate-yaml` — write YAML instead of JSON
- `--exclude dir1,dir2` — skip directories while walking the module
- `--quiet` — reduce log noise
- `--handler-path` — only scan handlers under this path
- `--strict` / `--debug` — stricter parse / verbose logs

Format annotations (like `go fmt` / `swag fmt`):

```shell
go-swagger3 fmt -d ./

Using docker

// go.mod and main file are in the same directory
docker run -t --rm -v $(pwd):/app -w /app parvez3019/go-swagger3:latest --module-path . --output oas.json --schema-without-pkg --generate-yaml true

// go.mod and main file are in the different directory
docker run -t --rm -v $(pwd):/app -w /app parvez3019/go-swagger3:latest --module-path . --main-file-path ./cmd/xxx/main.go --output oas.json --schema-without-pkg --generate-yaml true

Notes:
- `--schema-without-pkg` — schema names without package prefixes
- `--generate-yaml` — write YAML instead of JSON

3. Serving Swagger UI

Serve the generated OpenAPI file with an interactive UI at http://localhost:8080/swagger/index.html (same idea as swaggo/swag).

  1. Generate the spec:
go-swagger3 --module-path . --output oas.json --schema-without-pkg
  1. Mount the handler in your HTTP server:
package main

import (
	"log"
	"net/http"
	"os"

	"github.com/parvez3019/go-swagger3/swagger"
)

func main() {
	spec, err := os.ReadFile("oas.json")
	if err != nil {
		log.Fatal(err)
	}

	http.Handle("/swagger/", swagger.Handler(spec, swagger.Title("My API")))
	// your API routes...
	log.Println("Swagger UI at http://localhost:8080/swagger/index.html")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Or embed the file at build time:

import "embed"

//go:embed oas.json
var oas []byte

func main() {
	http.Handle("/swagger/", swagger.Handler(oas))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Or load from disk:

h, err := swagger.HandlerFromFile("oas.json")
if err != nil {
	log.Fatal(err)
}
http.Handle("/swagger/", h)

The handler serves:

Path Content
/swagger/index.html Swagger UI
/swagger/doc.json OpenAPI 3 document

Add the dependency:

go get github.com/parvez3019/go-swagger3/swagger

Supported Web Frameworks

Thin UI adapters wrap the core swagger.Handler (net/http). Each adapter is a separate module so you only pull the framework you use.

Framework Import Mount example
net/http github.com/parvez3019/go-swagger3/swagger http.Handle("/swagger/", swagger.Handler(spec))
gin .../swagger/gin r.Any("/swagger/*any", ginswagger.WrapHandler(spec))
echo .../swagger/echo e.Any("/swagger/*", echoswagger.WrapHandler(spec))
gorilla/mux .../swagger/mux r.PathPrefix("/swagger/").Handler(muxswagger.WrapHandler(spec))
go-chi/chi .../swagger/chi r.Handle("/swagger/*", chiswagger.WrapHandler(spec))
fiber .../swagger/fiber app.All("/swagger/*", fiberswagger.WrapHandler(spec))
hertz .../swagger/hertz see package docs
buffalo .../swagger/buffalo see package docs
flamingo .../swagger/flamingo http.Handler wrapper
atreugo .../swagger/atreugo see package docs

Minimal runnable samples: examples/.

Gin snippet:

import (
	"github.com/gin-gonic/gin"
	"github.com/parvez3019/go-swagger3/swagger"
	ginswagger "github.com/parvez3019/go-swagger3/swagger/gin"
)

r := gin.Default()
r.Any("/swagger/*any", ginswagger.WrapHandler(spec, swagger.Title("My API")))
go get github.com/parvez3019/go-swagger3/swagger/gin

4. Usage

You can document your service by placing annotations inside your godoc at various places in your code.

Service Description

The service description comments can be located in any of your .go files. They provide general information about the service you are documenting.

// @Version 1.0.0
// @Title Backend API
// @Description API usually works as expected. But sometimes its not true.
// @ContactName Parvez
// @ContactEmail abce@email.com
// @ContactURL http://someurl.oxox
// @TermsOfServiceUrl http://someurl.oxox
// @LicenseName MIT
// @LicenseURL https://en.wikipedia.org/wiki/MIT_License
// @Server http://www.fake.com Server-1
// @Server http://www.fake2.com Server-2
// @Security AuthorizationHeader read write
// @SecurityScheme AuthorizationHeader http bearer Input your token

Handler Functions

By adding comments to your handler func godoc, you can document individual actions as well as their input and output.

type User struct {
  ID   uint64 `json:"id" example:"100" description:"User identity"`
  Name string `json:"name" example:"Parvez"` 
}

type UsersResponse struct {
  Data []Users `json:"users" example:"[{\"id\":100, \"name\":\"Parvez\"}]"`
}

type Error struct {
  Code string `json:"code"`
  Msg  string `json:"msg" skip:"true"`
  // use skip:"true" if you want to skip the field in the documentation spec
}

type ErrorResponse struct {
  ErrorInfo Error `json:"error"`
}

// RequestHeaders represents the model for header params
// @HeaderParameters RequestHeaders
type RequestHeaders struct {
    Authorization  string  `json:"Authorization"`
}

// @Title Get user list of a group.
// @Description Get users related to a specific group.
// @Header model.RequestHeaders
// @Param  groupID  path  int  true  "Id of a specific group." "120"
// @Success  200  object  UsersResponse  "UsersResponse JSON"
// @Failure  400  object  ErrorResponse  "ErrorResponse JSON"
// @Resource users
// @Route /api/group/{groupID}/users [get]
func GetGroupUsers() {
  // ...
}

// @Title Get user list of a group.
// @Description Create a new user.
// @Param  user  body  User  true  "Info of a user."
// @Success  200  object  User           "UsersResponse JSON"
// @Failure  400  object  ErrorResponse  "ErrorResponse JSON"
// @Resource users
// @Route /api/user [post]
func PostUser() {
  // ...
}

Title And Description

@Title {title}
@Title Get user list of a group.

@Description {description}.
@Description Get users related to a specific group.
  • {title}: The title of the route.
  • {description}: The description of the route.

Parameter

@Param  {name}    {in}  {goType}  {required}  {description}             {example}
@Param  groupID   path  int       true        "Id of a specific group." "120"
  • {name}: The parameter name.
  • {in}: The parameter is in path, query, form, header, cookie, body or file.
  • {goType}: The type in go code. This will be ignored when {in} is file.
  • {required}: true, false, required or optional.
  • {description}: The description of the parameter. Must be quoted.
  • {example}: Optional example of this parameter. Must be quoted.

One can also override example for an object with override-example key in struct eg -

type Request struct {
    version  model.Version `"json:"Version" override-example:"11.0.0"`
}

Header

@Header          {goType}
@HeaderParameters   model.RequestHeaders
  • Header query param for endpoints, parses the query param from the model

Header Parameters

@Param              {goType}
@HeaderParameters   RequestHeaders
  • {goType}: The type in go code. This will be ignored when {in} is file.
  • Parses parameters from the type and keep it up component section for reference

Response Header

@ResponseHeader  {status}  {headerName}  {type}    "{description}"                                                    "{example}"
@ResponseHeader  200       Set-Cookie    string    "JWT token cookie"                                                 "accessToken=bzRslFay1i...; Path=/; HttpOnly; Secure; SameSite=Lax"
  • {status}: The HTTP status code of the response this header belongs to.
  • {headerName}: The name of the response header (e.g. Set-Cookie, X-Request-Id).
  • {type}: The primitive type of the header value. Supported values: string, integer, boolean, number.
  • {description}: The description of the header. Must be quoted.
  • {example}: Optional example value for the header. Must be quoted.

The @ResponseHeader annotation can be placed before or after the corresponding @Success/@Failure annotation — if the ResponseObject for the given status does not exist yet, it will be created automatically.

Multiple headers for the same status are supported:

// @Success      200        object        TokenResponse   "Login successful"
// @ResponseHeader  200  Set-Cookie    string  "Access token cookie"   "accessToken=bzRslFay1i...; Path=/; HttpOnly; Secure; SameSite=Lax"
// @ResponseHeader  200  X-Request-Id  string  "Unique request identifier"
func Login() {
  // ...
}

This produces the following OpenAPI output:

"200": {
  "description": "Login successful",
  "headers": {
    "Set-Cookie": {
      "description": "Access token cookie",
      "schema": {
        "type": "string",
        "example": "accessToken=bzRslFay1i...; Path=/; HttpOnly; Secure; SameSite=Lax"
      }
    },
    "X-Request-Id": {
      "description": "Unique request identifier",
      "schema": {
        "type": "string"
      }
    }
  },
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/TokenResponse"
      }
    }
  }
}

Response

@Success  {status}  {jsonType}  {goType}       {description}
@Success  200       object      UsersResponse  "UsersResponse JSON"

@Failure  {status}  {jsonType}  {goType}       {description}
@Failure  400       object      ErrorResponse  "ErrorResponse JSON"
  • {status}: The HTTP status code.
  • {jsonType}: The value can be object or array.
  • {goType}: The type in go code.
  • {description}: The description of the response. Must be quoted.

Resource & Tag

@Resource {resource}
@Resource users

@Tag {tag}
@tag xxx
  • {resource}, {tag}: Tag of the route.

Route

@Route {path}    {method}
@Route /api/user [post]
  • {path}: The URL path.
  • {method}: The HTTP Method. Must be put in brackets.

Enums

  • To generate enums create type structs for enum field with comma-separated values as follows:
  • Create struct type fields with @Enum Tag
  • Example as follows-
// @Enum CountriesEnum
type CountriesEnum struct {
    // Create the field name with same as struct name
    CountriesEnum string `enum:"india,china,mexico,japan" example:"india"`
}
How to add reference of Enum on types
type Request struct {
  Name string `json:"name" example:"Parvez"` 
  Country string `json:"country" $ref:"CountriesEnum"`
}

Response body

You need to provide swagger fields as reflect tags in your structure. In example:

type Filter struct {
	Rating       int    `json:"rating"`
	Type         string `json:"type"`
	Distance     int64  `json:"distance", minimum:"1", maximum:"100"`
	DistrictCode string `json:"district_code", pattern:"[a-z]{2}\\d[a-z]\\s\\d[a-z]{2}"`
}

Available fields:

  • type
  • format
  • required
  • properties
  • description
  • items
  • example
  • deprecated (bool)
  • ref
  • enum
  • title
  • maximum (float64)
  • exclusiveMaximum (bool)
  • minimum (float64)
  • exclusiveMinimum (bool)
  • maxLength (uint)
  • minLength (uint)
  • pattern
  • maxItems (uint)
  • minItems (uint)
  • uniqueItems (bool)
  • maxProperties (uint)
  • minProperties (uint)
  • additionalProperties (bool)
  • nullable (bool)
  • readOnly (bool)
  • writeOnly (bool)

5. Security

If authorization is required, you must define security schemes and then apply those to the API. A scheme is defined using @SecurityScheme [name] [type] [parameters] and applied by adding @Security [scheme-name] [scope1] [scope2] [...].

All examples in this section use MyApiAuth as the name. This name can be anything you chose; multiple named schemes are supported. Each scheme must have its own name, except for OAuth2 schemes - OAuth2 supports multiple schemes by the same name.

A number of different types is supported, they all have different parameters:

Type Description Parameters Example
HTTP A HTTP Authentication scheme using the Authorization header scheme: any HTTP Authentication scheme @SecurityScheme MyApiAuth basic
APIKey Authorization by passing an API Key along with the request in: Location of the API Key, options are header, query and cookie. name: The name of the field where the API Key must be set @SecurityScheme MyApiAuth apiKey header X-MyCustomHeader
OpenIdConnect Delegating security to a known OpenId server url: The URL of the OpenId server @SecurityScheme MyApiAuth openIdConnect https://example.com/.well-known/openid-configuration
OAuth2AuthCode Using the "Authentication Code" flow of OAuth2 authorizationUrl, tokenUrl @SecurityScheme MyApiAuth oauth2AuthCode /oauth/authorize /oauth/token
OAuth2Implicit Using the "Implicit" flow of OAuth2 authorizationUrl `@SecurityScheme MyApiAuth oauth2Implicit /oauth/authorize
OAuth2ResourceOwnerCredentials Using the "Resource Owner Credentials" flow of OAuth2 authorizationUrl `@SecurityScheme MyApiAuth oauth2ResourceOwnerCredentials /oauth/token
OAuth2ClientCredentials Using the "Client Credentials" flow of OAuth2 authorizationUrl `@SecurityScheme MyApiAuth oauth2ClientCredentials /oauth/token

Any text that is present after the last parameter wil be used as the description. For instance @SecurityScheme MyApiAuth basic Login with your admin credentials.

Once all security schemes have been defined, apply them with @Security. You can set global security on the service file and/or per-operation @Security on a handler.

// Global (service file)
// @Security MyApiAuth read_user write_user

// Per-operation (handler godoc)
// @Security MyApiAuth read_user

Scopes

For OAuth2 security schemes, it is possible to define scopes using the @SecurityScope [schema-name] [scope-code] [scope-description] comment.

// @SecurityScope MyApiAuth read_user Read a user from the system
// @SecurityScope MyApiAuth write_user Write a user to the system

6. Limitations

  • Go modules only.
  • Anonymous (unnamed) embedded struct fields have limited support; prefer named types.
  • OpenAPI output is 3.0.0 (3.1 mode planned; see migration guide).
  • Route discovery is annotation-driven (@Router / @Route); framework routers are not introspected.

7. References

Star History

Star History Chart