// Copyright (c) 2025-2026 Probo Inc . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package gqlutils import ( "context" "net/http" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler/extension" "github.com/99designs/gqlgen/graphql/handler/lru" "github.com/99designs/gqlgen/graphql/handler/transport" "github.com/vektah/gqlparser/v2/ast" "go.gearno.de/kit/log" ) type ( Handler struct { gqlhandler *handler.Server } // Limits bounds the per-request cost of a GraphQL operation to protect // against alias-flooding and other application-layer denial-of-service // vectors. A zero value disables the corresponding guard. Limits struct { ParserTokenLimit int ComplexityLimit int QueryCacheSize int DisableSuggestion bool } ) var ( mb int64 = 1024 * 1024 postTransport = transport.POST{} optionsTransport = transport.Options{} multipartTransport = transport.MultipartForm{ MaxMemory: 32 * mb, MaxUploadSize: 50 * mb, } introspectionExtension = extension.Introspection{} ) func NewHandler[S graphql.ExecutableSchema](executableSchema S, logger *log.Logger, limits Limits) *Handler { handler := handler.New(executableSchema) handler.AddTransport(postTransport) handler.AddTransport(optionsTransport) handler.AddTransport(multipartTransport) if limits.QueryCacheSize > 0 { handler.SetQueryCache(lru.New[*ast.QueryDocument](limits.QueryCacheSize)) } if limits.ParserTokenLimit > 0 { handler.SetParserTokenLimit(limits.ParserTokenLimit) } if limits.ComplexityLimit > 0 { handler.Use(extension.FixedComplexityLimit(limits.ComplexityLimit)) } handler.SetDisableSuggestion(limits.DisableSuggestion) handler.Use(introspectionExtension) handler.Use(NewTracingExtension(logger)) handler.SetRecoverFunc(RecoverFunc) return &Handler{gqlhandler: handler} } func (gqlh *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := WithHTTPContext(r.Context(), w, r) gqlh.gqlhandler.ServeHTTP(w, r.WithContext(ctx)) } func (gqlh *Handler) Use(extension graphql.HandlerExtension) { gqlh.gqlhandler.Use(extension) } func (gqlh *Handler) AroundOperations(f func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler) { gqlh.gqlhandler.AroundOperations(f) }