package main import ( "net/http" "tercul/internal/adapters/graphql" "tercul/internal/observability" "tercul/internal/platform/auth" "tercul/internal/platform/config" platform_http "tercul/internal/platform/http" gql "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler/extension" "github.com/99designs/gqlgen/graphql/playground" "github.com/prometheus/client_golang/prometheus" ) // NewAPIServer creates a new http.ServeMux and configures it with all the API routes, // including the GraphQL endpoint, GraphQL Playground, and Prometheus metrics. func NewAPIServer( cfg *config.Config, resolver *graphql.Resolver, queryCache gql.Cache[string], jwtManager *auth.JWTManager, metrics *observability.Metrics, logger *observability.Logger, reg *prometheus.Registry, ) *http.ServeMux { // Configure the GraphQL server c := graphql.Config{Resolvers: resolver} c.Directives.Binding = graphql.Binding // Create the core GraphQL handler graphqlHandler := handler.New(graphql.NewExecutableSchema(c)) // Enable Automatic Persisted Queries (APQ) if cache is provided if queryCache != nil { graphqlHandler.Use(extension.AutomaticPersistedQuery{ Cache: queryCache, }) } graphqlHandler.SetErrorPresenter(graphql.NewErrorPresenter()) // Create the middleware chain for the GraphQL endpoint. // Middlewares are applied from bottom to top (last applied is first executed). var chain http.Handler chain = graphqlHandler chain = metrics.PrometheusMiddleware(chain) chain = observability.LoggingMiddleware(logger)(chain) // Must run after auth and tracing chain = auth.GraphQLAuthMiddleware(jwtManager)(chain) chain = observability.TracingMiddleware(chain) chain = observability.RequestIDMiddleware(chain) // Security and Validation Middlewares chain = platform_http.RequestValidationMiddleware(chain) chain = platform_http.RateLimitMiddleware(cfg)(chain) // CORS should be the outermost to handle preflight OPTIONS requests // TODO: Make allowed origins configurable chain = platform_http.CORSMiddleware([]string{"*"})(chain) // Create a new ServeMux and register all handlers mux := http.NewServeMux() mux.Handle("/query", chain) mux.Handle("/playground", playground.Handler("GraphQL Playground", "/query")) mux.Handle("/metrics", observability.PrometheusHandler(reg)) return mux }