This package generates an OpenAPI document from comments in your code annotated with the @openapi keyword. It currently supports only the openapi 3.x schema and not the older swagger 2 schema.
openapi-godoc enables you to integrate OpenAPI using comments in your code. Add an @openapi line inside any Go comment and describe the given API part in YAML syntax beneath it. It's also possible to pass JSON snippets directly outside the annotated source code.
openapi-godoc will then parse the comments and output an OpenAPI document describing your API, allowing you to keep your API documentation as close as possible to your code thus maximizing the likelihood it stays up to date when your code changes.
@openapi is matched by line-start, so blocks can appear:
-
on a
structorfuncdeclaration, either as the first line of the doc comment or after one or more regular godoc lines (// MyFn does X.\n//\n// @openapi\n// …) -
in a free-standing comment group not attached to any declaration — useful for documenting routes whose handler lives in another package, or for grouping schema-only definitions. No special anchor is required: the parser scans every comment group in the file, so the block is picked up whether or not it attaches to a following declaration. Just leave a blank line between the block and the next declaration so the YAML doesn't become that declaration's godoc:
// @openapi // paths: // /health: // get: { responses: { '200': { description: ok } } } // Health reports liveness. The blank line above keeps the @openapi block // as its own comment group instead of this function's doc comment. func Health() {}
-
multiple times in a single comment group — each
@openapiline starts a new block, and the body runs until the next@openapiline or the end of the group.
- A marker is a comment line whose trimmed text is exactly
@openapi. Mid-line occurrences (e.g.// see @openapi for details) are ignored, so prose that mentions the keyword is safe. - Every comment group in every
.gofile under the scanned paths is searched — including_test.gofiles and comments inside function bodies. Keep stray@openapilines out of test and example files unless you intend them to land in the generated spec. - A block's body runs from the marker line to the next
@openapimarker in the same group, or the end of the group. Blank lines inside a body are preserved, so multi-line YAML works. - Parsing is fail-fast: a single malformed
@openapiblock anywhere aborts generation. The error names the offendingfile:lineso you can jump straight to it.
openapi-godoc does not add logic to your specification or generate client code from the OpenAPI document. It is based on code annotations and/or static JSON, not the code logic itself. It works only with what you put around your logic, not the contents of the logic.
go get github.com/tink3rlabs/openapi-godoc@latestAdd comments to your strucs and funcs that represent API components. Then define your API by creating an openapigodoc.OpenAPIDefinition object and providing general information about your API and OpenAPI specification components.
package main
import (
"log"
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/render"
openapigodoc "github.com/tink3rlabs/openapi-godoc"
)
// @openapi
// components:
//
// schemas:
// Message:
// type: object
// properties:
// content:
// type: string
// description: The contents of a message
// example: Hello world!
type Message struct {
Content string `json:"content"`
}
// @openapi
// components:
//
// responses:
// NotFound:
// description: The specified resource was not found
// content:
// application/json:
// schema:
// $ref: '#/components/schemas/Error'
// Unauthorized:
// description: Unauthorized
// content:
// application/json:
// schema:
// $ref: '#/components/schemas/Error'
// schemas:
// Error:
// type: object
// properties:
// status:
// type: string
// error:
// type: string
type ErrorResponse struct {
Status string `json:"status"`
Error string `json:"error"`
}
// @openapi
// paths:
//
// /:
// get:
// tags:
// - hello
// summary: Say Hello
// description: Returns a hello message
// operationId: sayHello
// responses:
// '200':
// description: successful operation
// content:
// application/json:
// schema:
// $ref: '#/components/schemas/Message'
func SayHello(w http.ResponseWriter, r *http.Request) {
msg := Message{
Content: "Hello world!",
}
render.JSON(w, r, msg)
}
func main() {
apiDefinition := openapigodoc.OpenAPIDefinition{
OpenAPI: "3.0.0",
Info: openapigodoc.Info{
Title: "Hello API",
Version: "1.0.0",
Description: "A hello world API",
},
Servers: []openapigodoc.Server{{URL: "http://localhost:8080"}},
Tags: []openapigodoc.Tag{
{
Name: "hello",
Description: "hello related apis",
},
},
ExternalDocs: openapigodoc.ExternalDocs{Description: "Find out more", URL: "http://example.com"},
}
openApiDoc, err := openapigodoc.GenerateOpenApiDoc(apiDefinition)
if err != nil {
log.Panicf("Logging err: %s\n", err.Error())
}
r := chi.NewRouter()
r.Use(
render.SetContentType(render.ContentTypeJSON),
middleware.RequestID,
middleware.Logger,
middleware.Recoverer,
cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: false,
MaxAge: 300, // Maximum value not ignored by any of major browsers
}),
)
r.Get("/", SayHello)
r.Get("/api-docs", func(w http.ResponseWriter, r *http.Request) {
w.Write(openApiDoc)
})
http.ListenAndServe(":8080", r)
}Additional API information can also be added directly to the openapigodoc.OpenAPIDefinition object's Components field. This is useful when you want to add general API definitions that don't directly relate to any structures or functions in the code base.
func main() {
securitySchemasData := []byte(`
{
"petstore_auth": {
"type": "oauth2",
"flows": {
"implicit": {
"authorizationUrl": "https://petstore3.swagger.io/oauth/authorize",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
}
}
},
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
}
}`)
var securitySchemas map[string]interface{}
err := json.Unmarshal(securitySchemasData, &securitySchemas)
if err != nil {
log.Panicf("Logging err: %s\n", err.Error()) // panic if there is an error
}
apiDefinition := openapigodoc.OpenAPIDefinition{
...
Components: openapigodoc.Components{
SecuritySchemes: securitySchemas,
},
}
openApiDoc, err := openapigodoc.GenerateOpenApiDoc(apiDefinition)
if err != nil {
log.Panicf("Logging err: %s\n", err.Error())
}
...
}Please see CONTRIBUTING. Thank you, contributors!
Released under the MIT License
This package was inspired by the excellent swagger-jsdoc library.