router/pkg/config/config.go (763 lines of code) (raw):

package config import ( "fmt" "os" "time" "github.com/caarlos0/env/v11" "github.com/goccy/go-yaml" "github.com/joho/godotenv" "github.com/wundergraph/cosmo/router/internal/unique" "github.com/wundergraph/cosmo/router/pkg/otel/otelconfig" ) const ( DefaultConfigPath = "config.yaml" ) type Graph struct { // Token is required if no router config path is provided Token string `yaml:"token,omitempty" env:"GRAPH_API_TOKEN"` // SignKey is used to validate the signature of the received config. The same key is used to publish the subgraph in sign mode. SignKey string `yaml:"sign_key,omitempty" env:"GRAPH_CONFIG_SIGN_KEY"` } type CustomStaticAttribute struct { Key string `yaml:"key"` Value string `yaml:"value"` } type CustomDynamicAttribute struct { RequestHeader string `yaml:"request_header,omitempty"` ContextField string `yaml:"context_field,omitempty"` ResponseHeader string `yaml:"response_header,omitempty"` Expression string `yaml:"expression,omitempty"` // only implemented by CustomAttribute in Metrics and Telemetry and Router Access Logs } type CustomAttribute struct { Key string `yaml:"key"` Default string `yaml:"default"` ValueFrom *CustomDynamicAttribute `yaml:"value_from,omitempty"` } type TracingExporterConfig struct { BatchTimeout time.Duration `yaml:"batch_timeout,omitempty" envDefault:"10s"` ExportTimeout time.Duration `yaml:"export_timeout,omitempty" envDefault:"30s"` } type TracingGlobalFeatures struct { ExportGraphQLVariables bool `yaml:"export_graphql_variables" envDefault:"false" env:"TRACING_EXPORT_GRAPHQL_VARIABLES"` WithNewRoot bool `yaml:"with_new_root" envDefault:"false" env:"TRACING_WITH_NEW_ROOT"` } type TracingExporter struct { Disabled bool `yaml:"disabled"` Exporter otelconfig.Exporter `yaml:"exporter,omitempty"` Endpoint string `yaml:"endpoint,omitempty"` HTTPPath string `yaml:"path,omitempty" envDefault:"/v1/traces"` Headers map[string]string `yaml:"headers,omitempty"` TracingExporterConfig `yaml:",inline"` } type ResponseTraceHeader struct { Enabled bool `yaml:"enabled"` HeaderName string `yaml:"header_name" envDefault:"x-wg-trace-id"` } type Tracing struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"TRACING_ENABLED"` SamplingRate float64 `yaml:"sampling_rate" envDefault:"1" env:"TRACING_SAMPLING_RATE"` ParentBasedSampler bool `yaml:"parent_based_sampler" envDefault:"true" env:"TRACING_PARENT_BASED_SAMPLER"` Exporters []TracingExporter `yaml:"exporters"` Propagation PropagationConfig `yaml:"propagation"` ResponseTraceHeader ResponseTraceHeader `yaml:"response_trace_id"` TracingGlobalFeatures `yaml:",inline"` } type PropagationConfig struct { TraceContext bool `yaml:"trace_context" envDefault:"true"` Jaeger bool `yaml:"jaeger"` B3 bool `yaml:"b3"` Baggage bool `yaml:"baggage"` Datadog bool `yaml:"datadog"` } type EngineStats struct { Subscriptions bool `yaml:"subscriptions" envDefault:"false" env:"ENGINE_STATS_SUBSCRIPTIONS"` } type Prometheus struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"PROMETHEUS_ENABLED"` Path string `yaml:"path" envDefault:"/metrics" env:"PROMETHEUS_HTTP_PATH"` ListenAddr string `yaml:"listen_addr" envDefault:"127.0.0.1:8088" env:"PROMETHEUS_LISTEN_ADDR"` GraphqlCache bool `yaml:"graphql_cache" envDefault:"false" env:"PROMETHEUS_GRAPHQL_CACHE"` EngineStats EngineStats `yaml:"engine_stats" envPrefix:"PROMETHEUS_"` ExcludeMetrics RegExArray `yaml:"exclude_metrics,omitempty" env:"PROMETHEUS_EXCLUDE_METRICS"` ExcludeMetricLabels RegExArray `yaml:"exclude_metric_labels,omitempty" env:"PROMETHEUS_EXCLUDE_METRIC_LABELS"` ExcludeScopeInfo bool `yaml:"exclude_scope_info" envDefault:"false" env:"PROMETHEUS_EXCLUDE_SCOPE_INFO"` } type MetricsOTLPExporter struct { Disabled bool `yaml:"disabled"` Exporter otelconfig.Exporter `yaml:"exporter" envDefault:"http"` Endpoint string `yaml:"endpoint"` HTTPPath string `yaml:"path" envDefault:"/v1/metrics"` Headers map[string]string `yaml:"headers"` Temporality otelconfig.ExporterTemporality `yaml:"temporality"` } type Metrics struct { Attributes []CustomAttribute `yaml:"attributes"` OTLP MetricsOTLP `yaml:"otlp"` Prometheus Prometheus `yaml:"prometheus"` } type MetricsOTLP struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"METRICS_OTLP_ENABLED"` RouterRuntime bool `yaml:"router_runtime" envDefault:"true" env:"METRICS_OTLP_ROUTER_RUNTIME"` GraphqlCache bool `yaml:"graphql_cache" envDefault:"false" env:"METRICS_OTLP_GRAPHQL_CACHE"` EngineStats EngineStats `yaml:"engine_stats" envPrefix:"METRICS_OTLP_"` ExcludeMetrics RegExArray `yaml:"exclude_metrics,omitempty" env:"METRICS_OTLP_EXCLUDE_METRICS"` ExcludeMetricLabels RegExArray `yaml:"exclude_metric_labels,omitempty" env:"METRICS_OTLP_EXCLUDE_METRIC_LABELS"` Exporters []MetricsOTLPExporter `yaml:"exporters"` } type Telemetry struct { ServiceName string `yaml:"service_name" envDefault:"cosmo-router" env:"TELEMETRY_SERVICE_NAME"` Attributes []CustomAttribute `yaml:"attributes"` ResourceAttributes []CustomStaticAttribute `yaml:"resource_attributes"` Tracing Tracing `yaml:"tracing"` Metrics Metrics `yaml:"metrics"` } type CORS struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"CORS_ENABLED"` AllowOrigins []string `yaml:"allow_origins" envDefault:"*" env:"CORS_ALLOW_ORIGINS"` AllowMethods []string `yaml:"allow_methods" envDefault:"HEAD,GET,POST" env:"CORS_ALLOW_METHODS"` AllowHeaders []string `yaml:"allow_headers" envDefault:"Origin,Content-Length,Content-Type" env:"CORS_ALLOW_HEADERS"` AllowCredentials bool `yaml:"allow_credentials" envDefault:"true" env:"CORS_ALLOW_CREDENTIALS"` MaxAge time.Duration `yaml:"max_age" envDefault:"5m" env:"CORS_MAX_AGE"` } type TrafficShapingRules struct { // All is a set of rules that apply to all requests All GlobalSubgraphRequestRule `yaml:"all"` // Apply to requests from clients to the router Router RouterTrafficConfiguration `yaml:"router"` // Subgraphs is a set of rules that apply to requests from the router to subgraphs. The key is the subgraph name. Subgraphs map[string]*GlobalSubgraphRequestRule `yaml:"subgraphs,omitempty"` } type FileUpload struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"FILE_UPLOAD_ENABLED"` MaxFileSizeBytes BytesString `yaml:"max_file_size" envDefault:"50MB" env:"FILE_UPLOAD_MAX_FILE_SIZE"` MaxFiles int `yaml:"max_files" envDefault:"10" env:"FILE_UPLOAD_MAX_FILES"` } type RouterTrafficConfiguration struct { // MaxRequestBodyBytes is the maximum size of the request body in bytes MaxRequestBodyBytes BytesString `yaml:"max_request_body_size" envDefault:"5MB"` // MaxHeaderBytes is the maximum size of the request headers in bytes MaxHeaderBytes BytesString `yaml:"max_header_bytes" envDefault:"0MiB" env:"MAX_HEADER_BYTES"` // DecompressionEnabled is the configuration for request compression DecompressionEnabled bool `yaml:"decompression_enabled" envDefault:"true"` } type GlobalSubgraphRequestRule struct { BackoffJitterRetry BackoffJitterRetry `yaml:"retry"` // See https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/ RequestTimeout *time.Duration `yaml:"request_timeout,omitempty" envDefault:"60s"` DialTimeout *time.Duration `yaml:"dial_timeout,omitempty" envDefault:"30s"` ResponseHeaderTimeout *time.Duration `yaml:"response_header_timeout,omitempty" envDefault:"0s"` ExpectContinueTimeout *time.Duration `yaml:"expect_continue_timeout,omitempty" envDefault:"0s"` TLSHandshakeTimeout *time.Duration `yaml:"tls_handshake_timeout,omitempty" envDefault:"10s"` KeepAliveIdleTimeout *time.Duration `yaml:"keep_alive_idle_timeout,omitempty" envDefault:"0s"` KeepAliveProbeInterval *time.Duration `yaml:"keep_alive_probe_interval,omitempty" envDefault:"30s"` // Connection configuration MaxConnsPerHost *int `yaml:"max_conns_per_host,omitempty" envDefault:"100"` MaxIdleConns *int `yaml:"max_idle_conns,omitempty" envDefault:"1024"` MaxIdleConnsPerHost *int `yaml:"max_idle_conns_per_host,omitempty" envDefault:"20"` } type SubgraphTrafficRequestRule struct { RequestTimeout time.Duration `yaml:"request_timeout,omitempty" envDefault:"60s"` } type GraphqlMetrics struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"GRAPHQL_METRICS_ENABLED"` CollectorEndpoint string `yaml:"collector_endpoint" envDefault:"https://cosmo-metrics.wundergraph.com" env:"GRAPHQL_METRICS_COLLECTOR_ENDPOINT"` } type BackoffJitterRetry struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"RETRY_ENABLED"` Algorithm string `yaml:"algorithm" envDefault:"backoff_jitter"` MaxAttempts int `yaml:"max_attempts" envDefault:"5"` MaxDuration time.Duration `yaml:"max_duration" envDefault:"10s"` Interval time.Duration `yaml:"interval" envDefault:"3s"` } type SubgraphCacheControlRule struct { Name string `yaml:"name"` Value string `yaml:"value"` } type CacheControlPolicy struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"CACHE_CONTROL_POLICY_ENABLED"` Value string `yaml:"value" env:"CACHE_CONTROL_POLICY_VALUE"` Subgraphs []SubgraphCacheControlRule `yaml:"subgraphs,omitempty"` } type HeaderRules struct { // All is a set of rules that apply to all requests All *GlobalHeaderRule `yaml:"all,omitempty"` Subgraphs map[string]*GlobalHeaderRule `yaml:"subgraphs,omitempty"` CookieWhitelist []string `yaml:"cookie_whitelist,omitempty"` } type GlobalHeaderRule struct { // Request is a set of rules that apply to requests Request []*RequestHeaderRule `yaml:"request,omitempty"` Response []*ResponseHeaderRule `yaml:"response,omitempty"` } type HeaderRuleOperation string const ( HeaderRuleOperationPropagate HeaderRuleOperation = "propagate" HeaderRuleOperationSet HeaderRuleOperation = "set" ) type HeaderRule interface { GetOperation() HeaderRuleOperation GetMatching() string } type RequestHeaderRule struct { // Operation describes the header operation to perform e.g. "propagate" Operation HeaderRuleOperation `yaml:"op"` // Propagate options // Matching is the regex to match the header name against Matching string `yaml:"matching"` // Named is the exact header name to match Named string `yaml:"named"` // Rename renames the header's key to the provided value Rename string `yaml:"rename,omitempty"` // Default is the default value to set if the header is not present Default string `yaml:"default"` // Set header options // Name is the name of the header to set Name string `yaml:"name"` // Value is the value of the header to set Value string `yaml:"value"` // ValueFrom is the context field to get the value from, in propagating to subgraphs ValueFrom *CustomDynamicAttribute `yaml:"value_from,omitempty"` } func (r *RequestHeaderRule) GetOperation() HeaderRuleOperation { return r.Operation } func (r *RequestHeaderRule) GetMatching() string { return r.Matching } type ResponseHeaderRuleAlgorithm string const ( // ResponseHeaderRuleAlgorithmFirstWrite propagates the first response header from a subgraph to the client ResponseHeaderRuleAlgorithmFirstWrite ResponseHeaderRuleAlgorithm = "first_write" // ResponseHeaderRuleAlgorithmLastWrite propagates the last response header from a subgraph to the client ResponseHeaderRuleAlgorithmLastWrite ResponseHeaderRuleAlgorithm = "last_write" // ResponseHeaderRuleAlgorithmAppend appends all response headers from all subgraphs to a comma separated list of values in the client response ResponseHeaderRuleAlgorithmAppend ResponseHeaderRuleAlgorithm = "append" // ResponseHeaderRuleAlgorithmMostRestrictiveCacheControl propagates the most restrictive cache control header from all subgraph responses to the client ResponseHeaderRuleAlgorithmMostRestrictiveCacheControl ResponseHeaderRuleAlgorithm = "most_restrictive_cache_control" ) type ResponseHeaderRule struct { // Operation describes the header operation to perform e.g. "propagate" Operation HeaderRuleOperation `yaml:"op"` // Matching is the regex to match the header name against Matching string `yaml:"matching"` // Named is the exact header name to match Named string `yaml:"named"` // Rename renames the header's key to the provided value Rename string `yaml:"rename,omitempty"` // Default is the default value to set if the header is not present Default string `yaml:"default"` // Algorithm is the algorithm to use when multiple headers are present Algorithm ResponseHeaderRuleAlgorithm `yaml:"algorithm,omitempty"` // Set header options // Name is the name of the header to set Name string `yaml:"name"` // Value is the value of the header to set Value string `yaml:"value"` } func (r *ResponseHeaderRule) GetOperation() HeaderRuleOperation { return r.Operation } func (r *ResponseHeaderRule) GetMatching() string { return r.Matching } type EngineDebugConfiguration struct { PrintOperationTransformations bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_OPERATION_TRANSFORMATIONS" yaml:"print_operation_transformations"` PrintOperationEnableASTRefs bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_OPERATION_ENABLE_AST_REFS" yaml:"print_operation_enable_ast_refs"` PrintPlanningPaths bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_PLANNING_PATHS" yaml:"print_planning_paths"` PrintQueryPlans bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_QUERY_PLANS" yaml:"print_query_plans"` PrintIntermediateQueryPlans bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_INTERMEDIATE_QUERY_PLANS" yaml:"print_intermediate_query_plans"` PrintNodeSuggestions bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_NODE_SUGGESTIONS" yaml:"print_node_suggestions"` ConfigurationVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_CONFIGURATION_VISITOR" yaml:"configuration_visitor"` PlanningVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_PLANNING_VISITOR" yaml:"planning_visitor"` DatasourceVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_DATASOURCE_VISITOR" yaml:"datasource_visitor"` ReportWebSocketConnections bool `envDefault:"false" env:"ENGINE_DEBUG_REPORT_WEBSOCKET_CONNECTIONS" yaml:"report_websocket_connections"` ReportMemoryUsage bool `envDefault:"false" env:"ENGINE_DEBUG_REPORT_MEMORY_USAGE" yaml:"report_memory_usage"` EnableResolverDebugging bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_RESOLVER_DEBUGGING" yaml:"enable_resolver_debugging"` EnablePersistedOperationsCacheResponseHeader bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_PERSISTED_OPERATIONS_CACHE_RESPONSE_HEADER" yaml:"enable_persisted_operations_cache_response_header"` EnableNormalizationCacheResponseHeader bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_NORMALIZATION_CACHE_RESPONSE_HEADER" yaml:"enable_normalization_cache_response_header"` AlwaysIncludeQueryPlan bool `envDefault:"false" env:"ENGINE_DEBUG_ALWAYS_INCLUDE_QUERY_PLAN" yaml:"always_include_query_plan"` AlwaysSkipLoader bool `envDefault:"false" env:"ENGINE_DEBUG_ALWAYS_SKIP_LOADER" yaml:"always_skip_loader"` } type EngineExecutionConfiguration struct { Debug EngineDebugConfiguration `yaml:"debug"` EnableSingleFlight bool `envDefault:"true" env:"ENGINE_ENABLE_SINGLE_FLIGHT" yaml:"enable_single_flight"` EnableRequestTracing bool `envDefault:"true" env:"ENGINE_ENABLE_REQUEST_TRACING" yaml:"enable_request_tracing"` EnableExecutionPlanCacheResponseHeader bool `envDefault:"false" env:"ENGINE_ENABLE_EXECUTION_PLAN_CACHE_RESPONSE_HEADER" yaml:"enable_execution_plan_cache_response_header"` MaxConcurrentResolvers int `envDefault:"1024" env:"ENGINE_MAX_CONCURRENT_RESOLVERS" yaml:"max_concurrent_resolvers,omitempty"` EnableNetPoll bool `envDefault:"true" env:"ENGINE_ENABLE_NET_POLL" yaml:"enable_net_poll"` WebSocketClientPollTimeout time.Duration `envDefault:"1s" env:"ENGINE_WEBSOCKET_CLIENT_POLL_TIMEOUT" yaml:"websocket_client_poll_timeout,omitempty"` WebSocketClientConnBufferSize int `envDefault:"128" env:"ENGINE_WEBSOCKET_CLIENT_CONN_BUFFER_SIZE" yaml:"websocket_client_conn_buffer_size,omitempty"` WebSocketClientReadTimeout time.Duration `envDefault:"5s" env:"ENGINE_WEBSOCKET_CLIENT_READ_TIMEOUT" yaml:"websocket_client_read_timeout,omitempty"` ExecutionPlanCacheSize int64 `envDefault:"1024" env:"ENGINE_EXECUTION_PLAN_CACHE_SIZE" yaml:"execution_plan_cache_size,omitempty"` MinifySubgraphOperations bool `envDefault:"true" env:"ENGINE_MINIFY_SUBGRAPH_OPERATIONS" yaml:"minify_subgraph_operations"` EnablePersistedOperationsCache bool `envDefault:"true" env:"ENGINE_ENABLE_PERSISTED_OPERATIONS_CACHE" yaml:"enable_persisted_operations_cache"` EnableNormalizationCache bool `envDefault:"true" env:"ENGINE_ENABLE_NORMALIZATION_CACHE" yaml:"enable_normalization_cache"` NormalizationCacheSize int64 `envDefault:"1024" env:"ENGINE_NORMALIZATION_CACHE_SIZE" yaml:"normalization_cache_size,omitempty"` OperationHashCacheSize int64 `envDefault:"2048" env:"ENGINE_OPERATION_HASH_CACHE_SIZE" yaml:"operation_hash_cache_size,omitempty"` ParseKitPoolSize int `envDefault:"16" env:"ENGINE_PARSEKIT_POOL_SIZE" yaml:"parsekit_pool_size,omitempty"` EnableValidationCache bool `envDefault:"true" env:"ENGINE_ENABLE_VALIDATION_CACHE" yaml:"enable_validation_cache"` ValidationCacheSize int64 `envDefault:"1024" env:"ENGINE_VALIDATION_CACHE_SIZE" yaml:"validation_cache_size,omitempty"` ResolverMaxRecyclableParserSize int `envDefault:"32768" env:"ENGINE_RESOLVER_MAX_RECYCLABLE_PARSER_SIZE" yaml:"resolver_max_recyclable_parser_size,omitempty"` EnableSubgraphFetchOperationName bool `envDefault:"false" env:"ENGINE_ENABLE_SUBGRAPH_FETCH_OPERATION_NAME" yaml:"enable_subgraph_fetch_operation_name"` DisableVariablesRemapping bool `envDefault:"false" env:"ENGINE_DISABLE_VARIABLES_REMAPPING" yaml:"disable_variables_remapping"` SubscriptionFetchTimeout time.Duration `envDefault:"30s" env:"ENGINE_SUBSCRIPTION_FETCH_TIMEOUT" yaml:"subscription_fetch_timeout,omitempty"` } type BlockOperationConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"ENABLED"` Condition string `yaml:"condition" env:"CONDITION"` } type SecurityConfiguration struct { BlockMutations BlockOperationConfiguration `yaml:"block_mutations" envPrefix:"SECURITY_BLOCK_MUTATIONS_"` BlockSubscriptions BlockOperationConfiguration `yaml:"block_subscriptions" envPrefix:"SECURITY_BLOCK_SUBSCRIPTIONS_"` BlockNonPersistedOperations BlockOperationConfiguration `yaml:"block_non_persisted_operations" envPrefix:"SECURITY_BLOCK_NON_PERSISTED_OPERATIONS_"` ComplexityCalculationCache *ComplexityCalculationCache `yaml:"complexity_calculation_cache"` ComplexityLimits *ComplexityLimits `yaml:"complexity_limits"` DepthLimit *QueryDepthConfiguration `yaml:"depth_limit"` } type QueryDepthConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"SECURITY_QUERY_DEPTH_ENABLED"` Limit int `yaml:"limit,omitempty" envDefault:"0" env:"SECURITY_QUERY_DEPTH_LIMIT"` CacheSize int64 `yaml:"cache_size,omitempty" envDefault:"1024" env:"SECURITY_QUERY_DEPTH_CACHE_SIZE"` IgnorePersistedOperations bool `yaml:"ignore_persisted_operations,omitempty" envDefault:"false" env:"SECURITY_QUERY_DEPTH_IGNORE_PERSISTED_OPERATIONS"` } type ComplexityCalculationCache struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"SECURITY_COMPLEXITY_CACHE_ENABLED"` CacheSize int64 `yaml:"size,omitempty" envDefault:"1024" env:"SECURITY_COMPLEXITY_CACHE_SIZE"` } type ComplexityLimits struct { Depth *ComplexityLimit `yaml:"depth"` TotalFields *ComplexityLimit `yaml:"total_fields"` RootFields *ComplexityLimit `yaml:"root_fields"` RootFieldAliases *ComplexityLimit `yaml:"root_field_aliases"` } type ComplexityLimit struct { Enabled bool `yaml:"enabled" envDefault:"false"` Limit int `yaml:"limit,omitempty" envDefault:"0"` IgnorePersistedOperations bool `yaml:"ignore_persisted_operations,omitempty" envDefault:"false"` } func (c *ComplexityLimit) ApplyLimit(isPersistent bool) bool { return c.Enabled && (!isPersistent || isPersistent && !c.IgnorePersistedOperations) } type OverrideRoutingURLConfiguration struct { Subgraphs map[string]string `yaml:"subgraphs"` } type SubgraphOverridesConfiguration struct { RoutingURL string `yaml:"routing_url"` SubscriptionURL string `yaml:"subscription_url"` SubscriptionProtocol string `yaml:"subscription_protocol"` SubscriptionWebsocketSubprotocol string `yaml:"subscription_websocket_subprotocol"` } type OverridesConfiguration struct { Subgraphs map[string]SubgraphOverridesConfiguration `yaml:"subgraphs"` } type JWKSConfiguration struct { URL string `yaml:"url"` Algorithms []string `yaml:"algorithms"` RefreshInterval time.Duration `yaml:"refresh_interval" envDefault:"1m"` } type HeaderSource struct { Type string `yaml:"type"` Name string `yaml:"name"` ValuePrefixes []string `yaml:"value_prefixes"` } type JWTAuthenticationConfiguration struct { JWKS []JWKSConfiguration `yaml:"jwks"` HeaderName string `yaml:"header_name" envDefault:"Authorization"` HeaderValuePrefix string `yaml:"header_value_prefix" envDefault:"Bearer"` HeaderSources []HeaderSource `yaml:"header_sources"` } type AuthenticationConfiguration struct { JWT JWTAuthenticationConfiguration `yaml:"jwt"` } type AuthorizationConfiguration struct { RequireAuthentication bool `yaml:"require_authentication" envDefault:"false" env:"REQUIRE_AUTHENTICATION"` // RejectOperationIfUnauthorized makes the router reject the whole GraphQL Operation if one field fails to authorize RejectOperationIfUnauthorized bool `yaml:"reject_operation_if_unauthorized" envDefault:"false" env:"REJECT_OPERATION_IF_UNAUTHORIZED"` } type RateLimitConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"RATE_LIMIT_ENABLED"` Strategy string `yaml:"strategy" envDefault:"simple" env:"RATE_LIMIT_STRATEGY"` SimpleStrategy RateLimitSimpleStrategy `yaml:"simple_strategy"` Storage RedisConfiguration `yaml:"storage"` // Debug ensures that retryAfter and resetAfter are set to stable values for testing // Debug also exposes the rate limit key in the response extension for debugging purposes Debug bool `yaml:"debug" envDefault:"false" env:"RATE_LIMIT_DEBUG"` KeySuffixExpression string `yaml:"key_suffix_expression,omitempty" env:"RATE_LIMIT_KEY_SUFFIX_EXPRESSION"` ErrorExtensionCode RateLimitErrorExtensionCode `yaml:"error_extension_code"` } type RateLimitErrorExtensionCode struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"RATE_LIMIT_ERROR_EXTENSION_CODE_ENABLED"` Code string `yaml:"code" envDefault:"RATE_LIMIT_EXCEEDED" env:"RATE_LIMIT_ERROR_EXTENSION_CODE"` } type RedisConfiguration struct { URLs []string `yaml:"urls,omitempty" env:"RATE_LIMIT_REDIS_URLS"` ClusterEnabled bool `yaml:"cluster_enabled,omitempty" envDefault:"false" env:"RATE_LIMIT_REDIS_CLUSTER_ENABLED"` KeyPrefix string `yaml:"key_prefix,omitempty" envDefault:"cosmo_rate_limit" env:"RATE_LIMIT_REDIS_KEY_PREFIX"` } type RateLimitSimpleStrategy struct { Rate int `yaml:"rate" envDefault:"10" env:"RATE_LIMIT_SIMPLE_RATE"` Burst int `yaml:"burst" envDefault:"10" env:"RATE_LIMIT_SIMPLE_BURST"` Period time.Duration `yaml:"period" envDefault:"1s" env:"RATE_LIMIT_SIMPLE_PERIOD"` RejectExceedingRequests bool `yaml:"reject_exceeding_requests" envDefault:"false" env:"RATE_LIMIT_SIMPLE_REJECT_EXCEEDING_REQUESTS"` RejectStatusCode int `yaml:"reject_status_code" envDefault:"200" env:"RATE_LIMIT_SIMPLE_REJECT_STATUS_CODE"` HideStatsFromResponseExtension bool `yaml:"hide_stats_from_response_extension" envDefault:"false" env:"RATE_LIMIT_SIMPLE_HIDE_STATS_FROM_RESPONSE_EXTENSION"` } type CDNConfiguration struct { URL string `yaml:"url" env:"CDN_URL" envDefault:"https://cosmo-cdn.wundergraph.com"` CacheSize BytesString `yaml:"cache_size,omitempty" env:"CDN_CACHE_SIZE" envDefault:"100MB"` } type NatsTokenBasedAuthentication struct { Token *string `yaml:"token,omitempty"` } type NatsCredentialsAuthentication struct { Password *string `yaml:"password,omitempty"` Username *string `yaml:"username,omitempty"` } type NatsAuthentication struct { UserInfo NatsCredentialsAuthentication `yaml:"user_info"` NatsTokenBasedAuthentication `yaml:"token,inline"` } type NatsEventSource struct { ID string `yaml:"id,omitempty"` URL string `yaml:"url,omitempty"` Authentication *NatsAuthentication `yaml:"authentication,omitempty"` } type KafkaSASLPlainAuthentication struct { Password *string `yaml:"password,omitempty"` Username *string `yaml:"username,omitempty"` } type KafkaAuthentication struct { SASLPlain KafkaSASLPlainAuthentication `yaml:"sasl_plain,omitempty"` } type KafkaTLSConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"false"` } type KafkaEventSource struct { ID string `yaml:"id,omitempty"` Brokers []string `yaml:"brokers,omitempty"` Authentication *KafkaAuthentication `yaml:"authentication,omitempty"` TLS *KafkaTLSConfiguration `yaml:"tls,omitempty"` } type EventProviders struct { Nats []NatsEventSource `yaml:"nats,omitempty"` Kafka []KafkaEventSource `yaml:"kafka,omitempty"` } type EventsConfiguration struct { Providers EventProviders `yaml:"providers,omitempty"` } type Cluster struct { Name string `yaml:"name,omitempty" env:"CLUSTER_NAME"` } type AbsintheProtocolConfiguration struct { // Enabled true if the Router should accept Requests over WebSockets using the Absinthe Protocol (Phoenix) Handler Enabled bool `yaml:"enabled" envDefault:"true" env:"WEBSOCKETS_ABSINTHE_ENABLED"` // HandlerPath is the path where the Absinthe Protocol Handler is mounted // On this specific path, the Router will accept WebSocket Requests using the Absinthe Protocol // even if the Sub-protocol is not set to "absinthe" // Legacy clients might not set the Sub-protocol Header, so this is a fallback HandlerPath string `yaml:"handler_path" envDefault:"/absinthe/socket" env:"WEBSOCKETS_ABSINTHE_HANDLER_PATH"` } type ComplianceConfig struct { AnonymizeIP AnonymizeIpConfiguration `yaml:"anonymize_ip,omitempty"` } type ExportTokenConfiguration struct { // Enabled true if the Router should export the token to the client request header Enabled bool `yaml:"enabled" envDefault:"true"` // HeaderKey is the name of the header where the token should be exported to HeaderKey string `yaml:"header_key,omitempty" envDefault:"Authorization"` } type WebSocketAuthenticationConfiguration struct { // Tells if the Router should look for the JWT Token in the initial payload of the WebSocket Connection FromInitialPayload InitialPayloadAuthenticationConfiguration `yaml:"from_initial_payload,omitempty"` } type InitialPayloadAuthenticationConfiguration struct { // When true the Router should look for the token in the initial payload of the WebSocket Connection Enabled bool `yaml:"enabled,omitempty" envDefault:"false"` // The key in the initial payload where the token is stored Key string `yaml:"key,omitempty" envDefault:"Authorization"` // ExportToken represents the configuration for exporting the token to the client request header. ExportToken ExportTokenConfiguration `yaml:"export_token"` } type WebSocketConfiguration struct { // Enabled true if the Router should accept Requests over WebSockets Enabled bool `yaml:"enabled" envDefault:"true" env:"WEBSOCKETS_ENABLED"` // AbsintheProtocol configuration for the Absinthe Protocol AbsintheProtocol AbsintheProtocolConfiguration `yaml:"absinthe_protocol,omitempty"` // ForwardUpgradeHeaders true if the Router should forward Upgrade Request Headers in the Extensions payload when starting a Subscription on a Subgraph ForwardUpgradeHeaders ForwardUpgradeHeadersConfiguration `yaml:"forward_upgrade_headers"` // ForwardUpgradeQueryParamsInExtensions true if the Router should forward Upgrade Request Query Parameters in the Extensions payload when starting a Subscription on a Subgraph ForwardUpgradeQueryParams ForwardUpgradeQueryParamsConfiguration `yaml:"forward_upgrade_query_params"` // ForwardInitialPayload true if the Router should forward the initial payload of a Subscription Request to the Subgraph ForwardInitialPayload bool `yaml:"forward_initial_payload" envDefault:"true" env:"WEBSOCKETS_FORWARD_INITIAL_PAYLOAD"` // Authentication configuration for the WebSocket Connection Authentication WebSocketAuthenticationConfiguration `yaml:"authentication,omitempty"` } type ForwardUpgradeHeadersConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"FORWARD_UPGRADE_HEADERS_ENABLED"` AllowList []string `yaml:"allow_list" envDefault:"Authorization" env:"FORWARD_UPGRADE_HEADERS_ALLOW_LIST"` } type ForwardUpgradeQueryParamsConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"FORWARD_UPGRADE_QUERY_PARAMS_ENABLED"` AllowList []string `yaml:"allow_list" envDefault:"Authorization" env:"FORWARD_UPGRADE_QUERY_PARAMS_ALLOW_LIST"` } type AnonymizeIpConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"SECURITY_ANONYMIZE_IP_ENABLED"` Method string `yaml:"method" envDefault:"redact" env:"SECURITY_ANONYMIZE_IP_METHOD"` } type TLSClientAuthConfiguration struct { CertFile string `yaml:"cert_file,omitempty" env:"TLS_CLIENT_AUTH_CERT_FILE"` Required bool `yaml:"required" envDefault:"false" env:"TLS_CLIENT_AUTH_REQUIRED"` } type TLSServerConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"TLS_SERVER_ENABLED"` CertFile string `yaml:"cert_file,omitempty" env:"TLS_SERVER_CERT_FILE"` KeyFile string `yaml:"key_file,omitempty" env:"TLS_SERVER_KEY_FILE"` ClientAuth TLSClientAuthConfiguration `yaml:"client_auth,omitempty"` } type TLSConfiguration struct { Server TLSServerConfiguration `yaml:"server"` } type SubgraphErrorPropagationMode string const ( SubgraphErrorPropagationModeWrapped SubgraphErrorPropagationMode = "wrapped" SubgraphErrorPropagationModePassthrough SubgraphErrorPropagationMode = "pass-through" ) type SubgraphErrorPropagationConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"SUBGRAPH_ERROR_PROPAGATION_ENABLED"` PropagateStatusCodes bool `yaml:"propagate_status_codes" envDefault:"false" env:"SUBGRAPH_ERROR_PROPAGATION_STATUS_CODES"` Mode SubgraphErrorPropagationMode `yaml:"mode" envDefault:"wrapped" env:"SUBGRAPH_ERROR_PROPAGATION_MODE"` RewritePaths bool `yaml:"rewrite_paths" envDefault:"true" env:"SUBGRAPH_ERROR_PROPAGATION_REWRITE_PATHS"` OmitLocations bool `yaml:"omit_locations" envDefault:"true" env:"SUBGRAPH_ERROR_PROPAGATION_OMIT_LOCATIONS"` OmitExtensions bool `yaml:"omit_extensions" envDefault:"false" env:"SUBGRAPH_ERROR_PROPAGATION_OMIT_EXTENSIONS"` AttachServiceName bool `yaml:"attach_service_name" envDefault:"true" env:"SUBGRAPH_ERROR_PROPAGATION_ATTACH_SERVICE_NAME"` DefaultExtensionCode string `yaml:"default_extension_code" envDefault:"DOWNSTREAM_SERVICE_ERROR" env:"SUBGRAPH_ERROR_PROPAGATION_DEFAULT_EXTENSION_CODE"` AllowedExtensionFields []string `yaml:"allowed_extension_fields" envDefault:"code" env:"SUBGRAPH_ERROR_PROPAGATION_ALLOWED_EXTENSION_FIELDS"` AllowedFields []string `yaml:"allowed_fields" env:"SUBGRAPH_ERROR_PROPAGATION_ALLOWED_FIELDS"` } type StorageProviders struct { S3 []S3StorageProvider `yaml:"s3,omitempty"` CDN []BaseStorageProvider `yaml:"cdn,omitempty"` Redis []RedisStorageProvider `yaml:"redis,omitempty"` } type PersistedOperationsStorageConfig struct { ProviderID string `yaml:"provider_id,omitempty" env:"PERSISTED_OPERATIONS_STORAGE_PROVIDER_ID"` ObjectPrefix string `yaml:"object_prefix,omitempty" env:"PERSISTED_OPERATIONS_STORAGE_OBJECT_PREFIX"` } type AutomaticPersistedQueriesStorageConfig struct { ProviderID string `yaml:"provider_id,omitempty" env:"APQ_STORAGE_PROVIDER_ID"` ObjectPrefix string `yaml:"object_prefix,omitempty" env:"APQ_STORAGE_OBJECT_PREFIX"` } type S3StorageProvider struct { ID string `yaml:"id,omitempty"` Endpoint string `yaml:"endpoint,omitempty"` AccessKey string `yaml:"access_key,omitempty"` SecretKey string `yaml:"secret_key,omitempty"` Bucket string `yaml:"bucket,omitempty"` Region string `yaml:"region,omitempty"` Secure bool `yaml:"secure,omitempty"` } type BaseStorageProvider struct { ID string `yaml:"id,omitempty"` URL string `yaml:"url,omitempty" envDefault:"https://cosmo-cdn.wundergraph.com"` } type RedisStorageProvider struct { ID string `yaml:"id,omitempty" env:"STORAGE_PROVIDER_REDIS_ID"` URLs []string `yaml:"urls,omitempty" env:"STORAGE_PROVIDER_REDIS_URLS"` ClusterEnabled bool `yaml:"cluster_enabled,omitempty" envDefault:"false" env:"STORAGE_PROVIDER_REDIS_CLUSTER_ENABLED"` } type PersistedOperationsCDNProvider struct { URL string `yaml:"url,omitempty" envDefault:"https://cosmo-cdn.wundergraph.com"` } type ExecutionConfigStorage struct { ProviderID string `yaml:"provider_id,omitempty" env:"PROVIDER_ID"` ObjectPath string `yaml:"object_path,omitempty" env:"OBJECT_PATH"` } type FallbackExecutionConfigStorage struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"ENABLED"` ProviderID string `yaml:"provider_id,omitempty" env:"PROVIDER_ID"` ObjectPath string `yaml:"object_path,omitempty" env:"OBJECT_PATH"` } type ExecutionConfigFile struct { Path string `yaml:"path,omitempty" env:"EXECUTION_CONFIG_FILE_PATH"` Watch bool `yaml:"watch,omitempty" envDefault:"false" env:"EXECUTION_CONFIG_FILE_WATCH"` WatchInterval time.Duration `yaml:"watch_interval,omitempty" envDefault:"1s" env:"EXECUTION_CONFIG_FILE_WATCH_INTERVAL"` } type ExecutionConfig struct { File ExecutionConfigFile `yaml:"file,omitempty"` Storage ExecutionConfigStorage `yaml:"storage,omitempty" envPrefix:"EXECUTION_CONFIG_STORAGE_"` FallbackStorage FallbackExecutionConfigStorage `yaml:"fallback_storage,omitempty" envPrefix:"EXECUTION_CONFIG_FALLBACK_STORAGE_"` } type PersistedOperationsCacheConfig struct { Size BytesString `yaml:"size,omitempty" env:"PERSISTED_OPERATIONS_CACHE_SIZE" envDefault:"100MB"` } type AutomaticPersistedQueriesCacheConfig struct { Size BytesString `yaml:"size,omitempty" env:"APQ_CACHE_SIZE" envDefault:"100MB"` TTL int `yaml:"ttl" env:"APQ_CACHE_TTL" envDefault:"-1"` } type PersistedOperationsConfig struct { LogUnknown bool `yaml:"log_unknown" env:"PERSISTED_OPERATIONS_LOG_UNKNOWN" envDefault:"false"` Safelist SafelistConfiguration `yaml:"safelist" envPrefix:"PERSISTED_OPERATIONS_SAFELIST_"` Cache PersistedOperationsCacheConfig `yaml:"cache"` Storage PersistedOperationsStorageConfig `yaml:"storage"` } type SafelistConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"ENABLED"` } type AutomaticPersistedQueriesConfig struct { Enabled bool `yaml:"enabled" env:"APQ_ENABLED" envDefault:"false"` Cache AutomaticPersistedQueriesCacheConfig `yaml:"cache"` Storage AutomaticPersistedQueriesStorageConfig `yaml:"storage"` } type AccessLogsConfig struct { Enabled bool `yaml:"enabled" env:"ACCESS_LOGS_ENABLED" envDefault:"true"` Buffer AccessLogsBufferConfig `yaml:"buffer,omitempty" env:"ACCESS_LOGS_BUFFER"` Output AccessLogsOutputConfig `yaml:"output,omitempty" env:"ACCESS_LOGS_OUTPUT"` Router AccessLogsRouterConfig `yaml:"router,omitempty" env:"ACCESS_LOGS_ROUTER"` Subgraphs AccessLogsSubgraphsConfig `yaml:"subgraphs,omitempty" env:"ACCESS_LOGS_SUBGRAPH"` } type AccessLogsBufferConfig struct { Enabled bool `yaml:"enabled" env:"ACCESS_LOGS_BUFFER_ENABLED" envDefault:"false"` // Size is the maximum number of log entries to buffer before flushing Size BytesString `yaml:"size" envDefault:"256KB" env:"ACCESS_LOGS_BUFFER_SIZE"` // FlushInterval is the maximum time to wait before flushing the buffer FlushInterval time.Duration `yaml:"flush_interval" envDefault:"10s" env:"ACCESS_LOGS_FLUSH_INTERVAL"` } type AccessLogsOutputConfig struct { Stdout AccessLogsStdOutOutputConfig `yaml:"stdout" env:"ACCESS_LOGS_OUTPUT_STDOUT"` File AccessLogsFileOutputConfig `yaml:"file,omitempty" env:"ACCESS_LOGS_FILE_OUTPUT"` } type AccessLogsStdOutOutputConfig struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"ACCESS_LOGS_OUTPUT_STDOUT_ENABLED"` } type AccessLogsFileOutputConfig struct { Enabled bool `yaml:"enabled" env:"ACCESS_LOGS_OUTPUT_FILE_ENABLED" envDefault:"false"` Path string `yaml:"path" env:"ACCESS_LOGS_FILE_OUTPUT_PATH" envDefault:"access.log"` } type AccessLogsRouterConfig struct { Fields []CustomAttribute `yaml:"fields,omitempty" env:"ACCESS_LOGS_ROUTER_FIELDS"` } type AccessLogsSubgraphsConfig struct { Enabled bool `yaml:"enabled" env:"ACCESS_LOGS_SUBGRAPH_ENABLED" envDefault:"false"` Fields []CustomAttribute `yaml:"fields,omitempty" env:"ACCESS_LOGS_SUBGRAPH_FIELDS"` } type ApolloCompatibilityFlags struct { EnableAll bool `yaml:"enable_all" envDefault:"false" env:"APOLLO_COMPATIBILITY_ENABLE_ALL"` ValueCompletion ApolloCompatibilityValueCompletion `yaml:"value_completion"` TruncateFloats ApolloCompatibilityTruncateFloats `yaml:"truncate_floats"` SuppressFetchErrors ApolloCompatibilitySuppressFetchErrors `yaml:"suppress_fetch_errors"` ReplaceUndefinedOpFieldErrors ApolloCompatibilityReplaceUndefinedOpFieldErrors `yaml:"replace_undefined_op_field_errors"` ReplaceInvalidVarErrors ApolloCompatibilityReplaceInvalidVarErrors `yaml:"replace_invalid_var_errors"` ReplaceValidationErrorStatus ApolloCompatibilityReplaceValidationErrorStatus `yaml:"replace_validation_error_status"` SubscriptionMultipartPrintBoundary ApolloCompatibilitySubscriptionMultipartPrintBoundary `yaml:"subscription_multipart_print_boundary"` } type ApolloCompatibilityValueCompletion struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_COMPATIBILITY_VALUE_COMPLETION_ENABLED"` } type ClientHeader struct { Name string `yaml:"name,omitempty"` Version string `yaml:"version,omitempty"` } type ApolloCompatibilityTruncateFloats struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_COMPATIBILITY_TRUNCATE_FLOATS_ENABLED"` } type ApolloCompatibilitySuppressFetchErrors struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_COMPATIBILITY_SUPPRESS_FETCH_ERRORS_ENABLED"` } type ApolloCompatibilityReplaceUndefinedOpFieldErrors struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_COMPATIBILITY_REPLACE_UNDEFINED_OP_FIELD_ERRORS_ENABLED"` } type ApolloCompatibilityReplaceInvalidVarErrors struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_COMPATIBILITY_REPLACE_INVALID_VAR_ERRORS_ENABLED"` } type ApolloCompatibilityReplaceValidationErrorStatus struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_COMPATIBILITY_REPLACE_VALIDATION_ERROR_STATUS_ENABLED"` } type ApolloCompatibilitySubscriptionMultipartPrintBoundary struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_COMPATIBILITY_SUBSCRIPTION_MULTIPART_PRINT_BOUNDARY_ENABLED"` } type ApolloRouterCompatibilityFlags struct { ReplaceInvalidVarErrors ApolloRouterCompatibilityReplaceInvalidVarErrors `yaml:"replace_invalid_var_errors"` SubrequestHTTPError ApolloRouterCompatibilitySubrequestHTTPError `yaml:"subrequest_http_error"` } type ApolloRouterCompatibilityReplaceInvalidVarErrors struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_ROUTER_COMPATIBILITY_REPLACE_INVALID_VAR_ERRORS_ENABLED"` } type ApolloRouterCompatibilitySubrequestHTTPError struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"APOLLO_ROUTER_COMPATIBILITY_SUBREQUEST_HTTP_ERROR_ENABLED"` } type CacheWarmupSource struct { Filesystem *CacheWarmupFileSystemSource `yaml:"filesystem,omitempty"` } type CacheWarmupFileSystemSource struct { Path string `yaml:"path" env:"CACHE_WARMUP_SOURCE_FILESYSTEM_PATH"` } type CacheWarmupCDNSource struct{} type CacheWarmupConfiguration struct { Enabled bool `yaml:"enabled" envDefault:"false" env:"CACHE_WARMUP_ENABLED"` Source CacheWarmupSource `yaml:"source" env:"CACHE_WARMUP_SOURCE"` Workers int `yaml:"workers" envDefault:"8" env:"CACHE_WARMUP_WORKERS"` ItemsPerSecond int `yaml:"items_per_second" envDefault:"50" env:"CACHE_WARMUP_ITEMS_PER_SECOND"` Timeout time.Duration `yaml:"timeout" envDefault:"30s" env:"CACHE_WARMUP_TIMEOUT"` } type Config struct { Version string `yaml:"version,omitempty" ignored:"true"` InstanceID string `yaml:"instance_id,omitempty" env:"INSTANCE_ID"` Graph Graph `yaml:"graph,omitempty"` Telemetry Telemetry `yaml:"telemetry,omitempty"` GraphqlMetrics GraphqlMetrics `yaml:"graphql_metrics,omitempty"` CORS CORS `yaml:"cors,omitempty"` Cluster Cluster `yaml:"cluster,omitempty"` Compliance ComplianceConfig `yaml:"compliance,omitempty"` TLS TLSConfiguration `yaml:"tls,omitempty"` CacheControl CacheControlPolicy `yaml:"cache_control_policy"` Modules map[string]interface{} `yaml:"modules,omitempty"` Headers HeaderRules `yaml:"headers,omitempty"` TrafficShaping TrafficShapingRules `yaml:"traffic_shaping,omitempty"` FileUpload FileUpload `yaml:"file_upload,omitempty"` AccessLogs AccessLogsConfig `yaml:"access_logs,omitempty"` ListenAddr string `yaml:"listen_addr" envDefault:"localhost:3002" env:"LISTEN_ADDR"` ControlplaneURL string `yaml:"controlplane_url" envDefault:"https://cosmo-cp.wundergraph.com" env:"CONTROLPLANE_URL"` PlaygroundConfig PlaygroundConfig `yaml:"playground,omitempty"` PlaygroundEnabled bool `yaml:"playground_enabled" envDefault:"true" env:"PLAYGROUND_ENABLED"` IntrospectionEnabled bool `yaml:"introspection_enabled" envDefault:"true" env:"INTROSPECTION_ENABLED"` QueryPlansEnabled bool `yaml:"query_plans_enabled" envDefault:"true" env:"QUERY_PLANS_ENABLED"` LogLevel string `yaml:"log_level" envDefault:"info" env:"LOG_LEVEL"` JSONLog bool `yaml:"json_log" envDefault:"true" env:"JSON_LOG"` ShutdownDelay time.Duration `yaml:"shutdown_delay" envDefault:"60s" env:"SHUTDOWN_DELAY"` GracePeriod time.Duration `yaml:"grace_period" envDefault:"30s" env:"GRACE_PERIOD"` PollInterval time.Duration `yaml:"poll_interval" envDefault:"10s" env:"POLL_INTERVAL"` PollJitter time.Duration `yaml:"poll_jitter" envDefault:"5s" env:"POLL_JITTER"` HealthCheckPath string `yaml:"health_check_path" envDefault:"/health" env:"HEALTH_CHECK_PATH"` ReadinessCheckPath string `yaml:"readiness_check_path" envDefault:"/health/ready" env:"READINESS_CHECK_PATH"` LivenessCheckPath string `yaml:"liveness_check_path" envDefault:"/health/live" env:"LIVENESS_CHECK_PATH"` GraphQLPath string `yaml:"graphql_path" envDefault:"/graphql" env:"GRAPHQL_PATH"` PlaygroundPath string `yaml:"playground_path" envDefault:"/" env:"PLAYGROUND_PATH"` Authentication AuthenticationConfiguration `yaml:"authentication,omitempty"` Authorization AuthorizationConfiguration `yaml:"authorization,omitempty"` RateLimit RateLimitConfiguration `yaml:"rate_limit,omitempty"` LocalhostFallbackInsideDocker bool `yaml:"localhost_fallback_inside_docker" envDefault:"true" env:"LOCALHOST_FALLBACK_INSIDE_DOCKER"` CDN CDNConfiguration `yaml:"cdn,omitempty"` DevelopmentMode bool `yaml:"dev_mode" envDefault:"false" env:"DEV_MODE"` Events EventsConfiguration `yaml:"events,omitempty"` CacheWarmup CacheWarmupConfiguration `yaml:"cache_warmup,omitempty"` RouterConfigPath string `yaml:"router_config_path,omitempty" env:"ROUTER_CONFIG_PATH"` RouterRegistration bool `yaml:"router_registration" env:"ROUTER_REGISTRATION" envDefault:"true"` OverrideRoutingURL OverrideRoutingURLConfiguration `yaml:"override_routing_url"` Overrides OverridesConfiguration `yaml:"overrides"` SecurityConfiguration SecurityConfiguration `yaml:"security,omitempty"` EngineExecutionConfiguration EngineExecutionConfiguration `yaml:"engine"` WebSocket WebSocketConfiguration `yaml:"websocket,omitempty"` SubgraphErrorPropagation SubgraphErrorPropagationConfiguration `yaml:"subgraph_error_propagation"` StorageProviders StorageProviders `yaml:"storage_providers"` ExecutionConfig ExecutionConfig `yaml:"execution_config"` PersistedOperationsConfig PersistedOperationsConfig `yaml:"persisted_operations"` AutomaticPersistedQueries AutomaticPersistedQueriesConfig `yaml:"automatic_persisted_queries"` ApolloCompatibilityFlags ApolloCompatibilityFlags `yaml:"apollo_compatibility_flags"` ApolloRouterCompatibilityFlags ApolloRouterCompatibilityFlags `yaml:"apollo_router_compatibility_flags"` ClientHeader ClientHeader `yaml:"client_header"` } type PlaygroundConfig struct { Enabled bool `yaml:"enabled" envDefault:"true" env:"PLAYGROUND_ENABLED"` Path string `yaml:"path" envDefault:"/" env:"PLAYGROUND_PATH"` ConcurrencyLimit int `yaml:"concurrency_limit,omitempty" envDefault:"10" env:"PLAYGROUND_CONCURRENCY_LIMIT"` } type LoadResult struct { Config Config DefaultLoaded bool } func LoadConfig(configFilePath string, envOverride string) (*LoadResult, error) { _ = godotenv.Load(".env.local") _ = godotenv.Load() if envOverride != "" { _ = godotenv.Overload(envOverride) } cfg := &LoadResult{ Config: Config{}, DefaultLoaded: true, } // Try to load the environment variables into the config err := env.Parse(&cfg.Config) if err != nil { return nil, err } // Read the custom config file var configFileBytes []byte if configFilePath == "" { configFilePath = os.Getenv("CONFIG_PATH") if configFilePath == "" { configFilePath = DefaultConfigPath } } isDefaultConfigPath := configFilePath == DefaultConfigPath configFileBytes, err = os.ReadFile(configFilePath) if err != nil { if isDefaultConfigPath { cfg.DefaultLoaded = false } else { return nil, fmt.Errorf("could not read custom config file %s: %w", configFilePath, err) } } if configFileBytes != nil { // Expand environment variables in the config file // and unmarshal it into the config struct configYamlData := os.ExpandEnv(string(configFileBytes)) if err := yaml.Unmarshal([]byte(configYamlData), &cfg.Config); err != nil { return nil, fmt.Errorf("failed to unmarshal router config: %w", err) } // Validate the config against the JSON schema configFileBytes = []byte(configYamlData) err = ValidateConfig(configFileBytes, JSONSchema) if err != nil { return nil, fmt.Errorf("router config validation error: %w", err) } // Unmarshal the final config if err := yaml.Unmarshal(configFileBytes, &cfg.Config); err != nil { return nil, err } } // Post-process the config if cfg.Config.DevelopmentMode { cfg.Config.JSONLog = false cfg.Config.SubgraphErrorPropagation.Enabled = true cfg.Config.SubgraphErrorPropagation.PropagateStatusCodes = true cfg.Config.SubgraphErrorPropagation.OmitLocations = false cfg.Config.SubgraphErrorPropagation.AllowedExtensionFields = unique.SliceElements(append(cfg.Config.SubgraphErrorPropagation.AllowedExtensionFields, "code", "stacktrace")) } return cfg, nil }