feat(graphql): normalise custom mount paths

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 18:08:27 +00:00
parent ee83aabca0
commit cdef85dcc9
2 changed files with 84 additions and 1 deletions

View file

@ -4,6 +4,7 @@ package api
import (
"net/http"
"strings"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/handler"
@ -35,7 +36,7 @@ func WithPlayground() GraphQLOption {
// The default path is "/graphql".
func WithGraphQLPath(path string) GraphQLOption {
return func(cfg *graphqlConfig) {
cfg.path = path
cfg.path = normaliseGraphQLPath(path)
}
}
@ -55,6 +56,22 @@ func mountGraphQL(r *gin.Engine, cfg *graphqlConfig) {
}
}
// normaliseGraphQLPath coerces custom GraphQL paths into a stable form.
// The path always begins with a single slash and never ends with one.
func normaliseGraphQLPath(path string) string {
path = strings.TrimSpace(path)
if path == "" {
return defaultGraphQLPath
}
path = "/" + strings.Trim(path, "/")
if path == "/" {
return defaultGraphQLPath
}
return path
}
// wrapHTTPHandler adapts a standard http.Handler to a Gin handler function.
func wrapHTTPHandler(h http.Handler) gin.HandlerFunc {
return func(c *gin.Context) {

View file

@ -192,6 +192,72 @@ func TestWithGraphQL_Good_CustomPath(t *testing.T) {
}
}
func TestWithGraphQL_Good_NormalisesCustomPath(t *testing.T) {
gin.SetMode(gin.TestMode)
e, err := api.New(api.WithGraphQL(newTestSchema(), api.WithGraphQLPath(" /gql/ "), api.WithPlayground()))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
srv := httptest.NewServer(e.Handler())
defer srv.Close()
body := `{"query":"{ name }"}`
resp, err := http.Post(srv.URL+"/gql", "application/json", strings.NewReader(body))
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected 200 at normalised /gql, got %d", resp.StatusCode)
}
pgResp, err := http.Get(srv.URL + "/gql/playground")
if err != nil {
t.Fatalf("playground request failed: %v", err)
}
defer pgResp.Body.Close()
if pgResp.StatusCode != http.StatusOK {
t.Fatalf("expected 200 at normalised /gql/playground, got %d", pgResp.StatusCode)
}
}
func TestWithGraphQL_Good_DefaultPathWhenEmptyCustomPath(t *testing.T) {
gin.SetMode(gin.TestMode)
e, err := api.New(api.WithGraphQL(newTestSchema(), api.WithGraphQLPath(""), api.WithPlayground()))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
srv := httptest.NewServer(e.Handler())
defer srv.Close()
body := `{"query":"{ name }"}`
resp, err := http.Post(srv.URL+"/graphql", "application/json", strings.NewReader(body))
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected 200 at default /graphql, got %d", resp.StatusCode)
}
pgResp, err := http.Get(srv.URL + "/graphql/playground")
if err != nil {
t.Fatalf("playground request failed: %v", err)
}
defer pgResp.Body.Close()
if pgResp.StatusCode != http.StatusOK {
t.Fatalf("expected 200 at default /graphql/playground, got %d", pgResp.StatusCode)
}
}
func TestWithGraphQL_Good_CombinesWithOtherMiddleware(t *testing.T) {
gin.SetMode(gin.TestMode)