Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration to V2 using V1 Middleware and chi #133

Closed
MatthiasScholzTW opened this issue Sep 27, 2023 · 1 comment · Fixed by #144
Closed

Migration to V2 using V1 Middleware and chi #133

MatthiasScholzTW opened this issue Sep 27, 2023 · 1 comment · Fixed by #144
Labels
bug Something isn't working

Comments

@MatthiasScholzTW
Copy link

Summary

When using the v1 middleware in v2 with chi as a router the application panics when accessing the /doc endpoint. Hence I am wondering if I missed something during the initialization of the middleware?

What I did

Following the documentation it is mentioned the v1 chi middleware is compatible to v2 and can be used accordingly.

🐳 Huma v1 middleware is compatible with Chi, so if you use that router with v2 you can continue to use the v1 middleware in a v2 application.

Given this I came up with this integration:

// related import
import (
	"github.com/danielgtaylor/huma/middleware"
	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humachi"
)

func main() {
...
	cli := huma.NewCLI(func(hooks huma.Hooks, opts *Options) {
		// Create a new router & API
		var api huma.API
		router := chi.NewMux()
		router.Use(middleware.DefaultChain)
		config := huma.DefaultConfig("My API", "1.0.0")
		api = humachi.New(router, config)
                 ...
	})

	// Run the CLI. When passed no commands, it starts the server.
	cli.Run()
}

Unfortunately the application crashes when accessing the /doc endpoint.

Error investigation

The panic occurs in the go-chi package when trying to access the context which is nil.
The context is access within the logging part of the middleware: middleware/logger.go.


Reproduction sample

The full example source code:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"git.i.mercedes-benz.com/cxp/transit-service-api-aws/pkg/types/response"

	"git.i.mercedes-benz.com/cxp/transit-service-api-aws/internal/billing"
	"git.i.mercedes-benz.com/cxp/transit-service-api-aws/internal/storage"
	"git.i.mercedes-benz.com/cxp/transit-service-api-aws/internal/storage/dummy"

	"github.com/danielgtaylor/huma/middleware"
	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humachi"
	"github.com/go-chi/chi/v5"
	"github.com/spf13/cobra"
)

// Options for the CLI.
type Options struct {
	Port int `help:"Port to listen on" default:"8888"`
	Debug bool `doc:"Enable debug logging" default:"true"`
}

// GreetingInput represents the greeting operation request.
type GreetingInput struct {
	Name string `path:"name" doc:"Name to greet"`
}

// GreetingOutput represents the greeting operation response.
type GreetingOutput struct {
	Body struct {
		Message string `json:"message" doc:"Greeting message" example:"Hello, world!"`
	}
}

func main() {
	// Create a CLI app which takes a port option.
	cli := huma.NewCLI(func(hooks huma.Hooks, opts *Options) {
		// Create a new router & API
		var api huma.API
		router := chi.NewMux()
		router.Use(middleware.DefaultChain)
		config := huma.DefaultConfig("My API", "1.0.0")
		api = humachi.New(router, config)

		// Register GET /greeting/{name}
		huma.Register(api, huma.Operation{
			OperationID: "get-greeting",
			Summary:     "Get a greeting",
			Method:      http.MethodGet,
			Path:        "/greeting/{name}",
		}, func(ctx context.Context, input *GreetingInput) (*GreetingOutput, error) {
			resp := &GreetingOutput{}
			resp.Body.Message = fmt.Sprintf("Hello, %s!", input.Name)
			return resp, nil
		})

		srv := &http.Server{
			Addr:    fmt.Sprintf("%s:%d", "localhost", opts.Port),
			Handler: router,
		}

		// Tell the CLI how to start your router.
		hooks.OnStart(func() {
			// Start the server
			log.Printf("Server is running with: debug:%v host: %v port: %v\n", opts.Debug, "localhost", opts.Port)

			err := srv.ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				log.Fatalf("listen: %s\n", err)
			}
		})
	})

	// Run the CLI. When passed no commands, it starts the server.
	cli.Run()
}
@danielgtaylor
Copy link
Owner

@MatthiasScholzTW FYI, this was due to the switch to Chi v5, while Huma v1 uses Chi v4. The change in major version caused older middleware to no longer work (likely due to private request context information used by each version being treated as unique keys).

I've added a humachi.NewV4(...) which you can use instead to get back the old behavior as well as an example showing that it works. Sorry for the delay in fixing this, and I will publish a beta 3 soon with the fix, and a final Huma v2 hopefully in the next couple weeks after we finish some more company-internal testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants