github.com/swaggest/openapi-goを使ってOpenAPIを生成する

github.com/swaggest/openapi-goを使うとGoのコードからOpenAPIの定義(json/yaml)を生成できる。

pkg.go.dev

例:

package main

import (
    "encoding/json"
    "fmt"
    "log"

    openapi "github.com/swaggest/openapi-go"
    "github.com/swaggest/openapi-go/openapi3"
)

type endpoint struct {
    id        string
    method    string
    path      string
    request   any
    responses []response
}

type response struct {
    status int
    body   any
}

func main() {
    r := openapi3.Reflector{
        Spec: &openapi3.Spec{
            Openapi:    "3.0.3",
            Components: &openapi3.Components{},
        },
    }

    r.Spec.Info.
        WithTitle("Foo API").
        WithDescription("Foo API Document.").
        WithVersion("1.0")

    list := []endpoint{
        {
            id:      "PostFoo",
            method:  "POST",
            path:    "/foo",
            request: new(PostFooRequest),
            responses: []response{
                {status: 201, body: new(Foo)},
                {status: 400, body: new(ErrorResponse)},
            },
        },
        {
            id:      "GetFoo",
            method:  "GET",
            path:    "/foo/{id}",
            request: new(GetFooRequest),
            responses: []response{
                {status: 200, body: new(Foo)},
                {status: 404, body: new(ErrorResponse)},
            },
        },
    }

    for _, v := range list {
        oc, err := r.NewOperationContext(v.method, v.path)
        if err != nil {
            log.Println(err)
            continue
        }

        oc.SetID(v.id)
        oc.AddReqStructure(v.request)
        for _, resp := range v.responses {
            oc.AddRespStructure(resp.body, openapi.WithHTTPStatus(resp.status))
        }

        if err := r.AddOperation(oc); err != nil {
            log.Println(err)
            continue
        }
    }

    // b, err := r.Spec.MarshalYAML()
    b, err := json.MarshalIndent(r.Spec, "", " ")
    if err != nil {
        log.Println(err)
        return
    }

    fmt.Println(string(b))
}

type PostFooRequest struct {
    Data string `json:"data"`
}

type GetFooRequest struct {
    ID string `json:"id" path:"id"`
}

type Foo struct {
    ID   string `json:"id"`
    Data string `json:"data"`
}

type ErrorResponse struct {
    Code    string `json:"code"`
    Message string `json:"message"`
}

list の中身を既存コードから生成すれば途中からでも比較的簡単にOpenAPIを始められる。