DEV Community

Philip Perry
Philip Perry

Posted on

Versioning in Go Huma

We want to have a separate documentation for each version in Go Huma, e.g. /v1/docs, /v2/docs, etc.

This can be done by setting the docs path like this:

config.DocsPath = "/{version}/docs"
Enter fullscreen mode Exit fullscreen mode

We can use middleware to get the version from the path in the request and load the description used in the docs depending on the version:

config := huma.DefaultConfig("API V"+versionNumberString, versionNumberString+".0.0")
                overviewFilePath := filepath.Join("/app/docs", fmt.Sprintf("v%s", versionNumberString), "overview.md")
                overview, err := ioutil.ReadFile(overviewFilePath)
                if err != nil {
                    log.Fatalf("Error reading file: %v", err)
                }

                config.Info.Description = string(overview)
Enter fullscreen mode Exit fullscreen mode

And we can import all routes that we want to display in the docs based on the version. This will also load them so that they can be used as actual endpoints.

This is the complete code for routing:

router := chi.NewMux()
    router.Use(func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            urlPathParts := strings.Split(r.URL.Path, "/")
            versions := []string{"v1", "v2", "v3"}
            if helpers.Contains(versions, urlPathParts[1]) {
                versionPath := urlPathParts[1]
                if versionPath == "" {
                    http.Error(w, "version does not exist", http.StatusInternalServerError)
                }
                versionNumberString := strings.TrimPrefix(versionPath, "v")
                versionNumber, _ := strconv.Atoi(versionNumberString)
                config := huma.DefaultConfig("API V"+versionNumberString, versionNumberString+".0.0")
                overviewFilePath := fmt.Sprintf("docs/v%s/overview.md", versionNumberString)
                overview, err := ioutil.ReadFile(overviewFilePath)
                if err != nil {
                    log.Fatalf("Error reading file: %v", err)
                }

                config.Info.Description = string(overview)
                api := humachi.New(router, config)

                switch versionNumber {
                case 1:
                    api = v1handlers.AddV1Middleware(api)
                    v1handlers.AddV1Routes(api)
                case 2:
                    api = v2handlers.AddV2Middleware(api)
                    v2handlers.AddV2Routes(api)
                default:
                    api = v3handlers.AddV3Middleware(api)
                    router = v3handlers.AddV3ErrorResponses(router)
                    v3handlers.AddV3Routes(api)
                }
            }

            next.ServeHTTP(w, r)
        })
    })

    config := huma.DefaultConfig("API V3", "3.0.0")
    config.DocsPath = "/{version}/docs"

    humachi.New(router, config)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)