Documentation
¶
Overview ¶
Package graphql exposes graphdb over GraphQL with per-tenant schema isolation.
GenerateSchemaForTenant builds a schema scoped to a single tenant's labels so introspection cannot leak other tenants' metadata; GenerateSchema is the tenant-blind variant for single-tenant or CLI use. (Richer variants add mutations, filtering, aggregation, depth/complexity limits — see the GenerateSchemaWith* functions.) NewGraphQLHandler wraps a schema as an HTTP handler and threads the request context through to resolvers, which read the caller's tenant from it.
Node and edge property values serialize as a JSON string built through the shared storage converter (storage.PropertiesToJSON), so arrays, nested objects, and null round-trip correctly.
Index ¶
- Constants
- func ExecuteQuery(ctx context.Context, query string, schema graphql.Schema) *graphql.Result
- func ExecuteQueryWithVariables(ctx context.Context, query string, schema graphql.Schema, ...) *graphql.Result
- func ExecuteWithComplexity(schema graphql.Schema, query string, maxComplexity int, ...) *graphql.Result
- func ExecuteWithDepthLimit(schema graphql.Schema, query string, maxDepth int, ...) *graphql.Result
- func GenerateSchema(gs *storage.GraphStorage) (graphql.Schema, error)
- func GenerateSchemaForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
- func GenerateSchemaWithAggregation(gs *storage.GraphStorage) (graphql.Schema, error)
- func GenerateSchemaWithAggregationForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
- func GenerateSchemaWithComplexity(gs *storage.GraphStorage, config *ComplexityConfig) (graphql.Schema, error)
- func GenerateSchemaWithCursors(gs *storage.GraphStorage) (graphql.Schema, error)
- func GenerateSchemaWithDepthLimit(gs *storage.GraphStorage, maxDepth int) (graphql.Schema, error)
- func GenerateSchemaWithEdges(gs *storage.GraphStorage) (graphql.Schema, error)
- func GenerateSchemaWithEdgesForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
- func GenerateSchemaWithFiltering(gs *storage.GraphStorage) (graphql.Schema, error)
- func GenerateSchemaWithFilteringForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
- func GenerateSchemaWithLimits(gs *storage.GraphStorage, config *LimitConfig) (graphql.Schema, error)
- func GenerateSchemaWithLimitsForTenant(gs *storage.GraphStorage, config *LimitConfig, tenantID string, ...) (graphql.Schema, error)
- func GenerateSchemaWithMutations(gs *storage.GraphStorage) (graphql.Schema, error)
- func GenerateSchemaWithMutationsForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
- func GenerateSchemaWithSearch(gs *storage.GraphStorage, searchIndex *search.FullTextIndex, deps *MaskingDeps) (graphql.Schema, error)
- func GenerateSchemaWithSorting(gs *storage.GraphStorage) (graphql.Schema, error)
- func GenerateSchemaWithSubscriptions(gs *storage.GraphStorage, ps *pubsub.PubSub) (graphql.Schema, error)
- func GetDepthLimit(schema *graphql.Schema) (int, bool)
- func PublishEdgeCreated(ps *pubsub.PubSub, edge *storage.Edge)
- func PublishEdgeDeleted(ps *pubsub.PubSub, edge *storage.Edge)
- func PublishEdgeUpdated(ps *pubsub.PubSub, edge *storage.Edge)
- func PublishNodeCreated(ps *pubsub.PubSub, node *storage.Node)
- func PublishNodeDeleted(ps *pubsub.PubSub, node *storage.Node)
- func PublishNodeUpdated(ps *pubsub.PubSub, node *storage.Node)
- func RegisterDepthLimitedSchema(wrapper *DepthLimitedSchema)
- func SubscribeToEdgeCreated(ps *pubsub.PubSub, ctx context.Context, edgeType string) (*pubsub.Subscription, error)
- func SubscribeToEdgeDeleted(ps *pubsub.PubSub, ctx context.Context, edgeID uint64) (*pubsub.Subscription, error)
- func SubscribeToEdgeUpdated(ps *pubsub.PubSub, ctx context.Context, edgeID uint64) (*pubsub.Subscription, error)
- func SubscribeToNodeCreated(ps *pubsub.PubSub, ctx context.Context, label string) (*pubsub.Subscription, error)
- func SubscribeToNodeDeleted(ps *pubsub.PubSub, ctx context.Context, nodeID uint64) (*pubsub.Subscription, error)
- func SubscribeToNodeUpdated(ps *pubsub.PubSub, ctx context.Context, nodeID uint64) (*pubsub.Subscription, error)
- func ValidateComplexityConfig(config *ComplexityConfig) error
- func ValidateLimitConfig(config *LimitConfig) error
- func ValidateQueryComplexity(query string, config *ComplexityConfig, variableValues map[string]any) (int, error)
- func ValidateQueryDepth(query string, maxDepth int) error
- type BatchFunc
- type ComplexityConfig
- type DataLoader
- type DataLoaderConfig
- type DataLoaderContext
- type DepthLimitedSchema
- type EdgeEvent
- type FilterCondition
- type FilterExpression
- type GraphQLError
- type GraphQLHandler
- type GraphQLRequest
- type GraphQLResponse
- type LimitConfig
- type MaskingDeps
- type NodeEvent
- type OrderByInput
Constants ¶
const ( TopicNodeCreated = "node.created" TopicNodeUpdated = "node.updated" TopicNodeDeleted = "node.deleted" TopicEdgeCreated = "edge.created" TopicEdgeUpdated = "edge.updated" TopicEdgeDeleted = "edge.deleted" )
Topic names for subscriptions
Variables ¶
This section is empty.
Functions ¶
func ExecuteQuery ¶
ExecuteQuery executes a GraphQL query against a schema. The ctx is threaded into graphql.Params so resolvers can read it via p.Context — used by audit A6c-graphql-resolvers (the next PR) to extract the caller's tenant ID.
Audit A6c-graphql-ctx (2026-05-08): pre-fix, this function dropped the request context entirely; resolvers ran with context.Background(), so JWT-derived tenant scoping was invisible to GraphQL.
func ExecuteQueryWithVariables ¶
func ExecuteQueryWithVariables(ctx context.Context, query string, schema graphql.Schema, variables map[string]any) *graphql.Result
ExecuteQueryWithVariables executes a GraphQL query with variables. See ExecuteQuery for the rationale on the ctx parameter.
func ExecuteWithComplexity ¶
func ExecuteWithComplexity(schema graphql.Schema, query string, maxComplexity int, variableValues map[string]any) *graphql.Result
ExecuteWithComplexity executes a GraphQL query with complexity validation
func ExecuteWithDepthLimit ¶
func ExecuteWithDepthLimit(schema graphql.Schema, query string, maxDepth int, variableValues map[string]any) *graphql.Result
ExecuteWithDepthLimit executes a GraphQL query with depth validation
func GenerateSchema ¶
func GenerateSchema(gs *storage.GraphStorage) (graphql.Schema, error)
GenerateSchema generates a GraphQL schema from the storage layer (tenant-blind — discovers labels across every tenant). Used by CLI and single-tenant deployments. API callers should use GenerateSchemaForTenant so introspection (`__schema`) doesn't leak foreign-tenant label names — see audit A9 (#36).
Masking is disabled (deps = nil). CLI-mode builds don't need per-tenant masking — single-tenant deployments have no tenant boundary to mask across.
func GenerateSchemaForTenant ¶
func GenerateSchemaForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
GenerateSchemaForTenant generates a GraphQL schema scoped to the given tenant's labels. Audit A9 (2026-05-08): closes the introspection metadata leak where a tenant-A caller running `{ __schema { types } }` would see every other tenant's labels.
Resolver closures inside the schema already extract tenantID from p.Context via A6c-graphql-resolvers (#24), so query-result scoping was already correct; this fix is purely about the introspection / type-registry surface.
deps is the per-server masking hookup; nil disables masking. Production callers pass non-nil deps; tests pass nil.
func GenerateSchemaWithAggregation ¶
func GenerateSchemaWithAggregation(gs *storage.GraphStorage) (graphql.Schema, error)
GenerateSchemaWithAggregation generates a GraphQL schema with aggregation support (tenant-blind). API callers should use GenerateSchemaWithAggregationForTenant per audit A9 (#36).
Masking is disabled (deps = nil). Property discovery samples across all tenants, which is correct for this tenant-blind / single-tenant schema.
func GenerateSchemaWithAggregationForTenant ¶
func GenerateSchemaWithAggregationForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
GenerateSchemaWithAggregationForTenant scopes the schema to one tenant's labels. Audit A9.
deps is the F3 masking hookup; nil disables masking. Property discovery is scoped to the requesting tenant's own nodes so another tenant's property-key names never surface in this tenant's schema introspection (the schema-side counterpart of the A6c resolver-scoping).
func GenerateSchemaWithComplexity ¶
func GenerateSchemaWithComplexity(gs *storage.GraphStorage, config *ComplexityConfig) (graphql.Schema, error)
GenerateSchemaWithComplexity generates a GraphQL schema with complexity analysis
func GenerateSchemaWithCursors ¶
func GenerateSchemaWithCursors(gs *storage.GraphStorage) (graphql.Schema, error)
GenerateSchemaWithCursors generates a GraphQL schema with cursor-based pagination
func GenerateSchemaWithDepthLimit ¶
GenerateSchemaWithDepthLimit generates a GraphQL schema with query depth limiting
func GenerateSchemaWithEdges ¶
func GenerateSchemaWithEdges(gs *storage.GraphStorage) (graphql.Schema, error)
GenerateSchemaWithEdges generates a GraphQL schema with edge traversal capabilities (tenant-blind). API callers should use GenerateSchemaWithEdgesForTenant per audit A9 (#36).
Masking is disabled (deps = nil).
func GenerateSchemaWithEdgesForTenant ¶
func GenerateSchemaWithEdgesForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
GenerateSchemaWithEdgesForTenant scopes the schema's type registry to one tenant's labels. Audit A9 (2026-05-08) — closes the introspection metadata leak.
deps is the F3 masking hookup; nil disables masking.
func GenerateSchemaWithFiltering ¶
func GenerateSchemaWithFiltering(gs *storage.GraphStorage) (graphql.Schema, error)
GenerateSchemaWithFiltering generates a GraphQL schema with filtering support (tenant-blind). API callers should use GenerateSchemaWithFilteringForTenant per audit A9 (#36).
Masking is disabled (deps = nil).
func GenerateSchemaWithFilteringForTenant ¶
func GenerateSchemaWithFilteringForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
GenerateSchemaWithFilteringForTenant scopes the schema to one tenant's labels. Audit A9.
deps is the F3 masking hookup; nil disables masking.
func GenerateSchemaWithLimits ¶
func GenerateSchemaWithLimits(gs *storage.GraphStorage, config *LimitConfig) (graphql.Schema, error)
GenerateSchemaWithLimits generates a GraphQL schema with filtering and result limits (tenant-blind). API callers should use GenerateSchemaWithLimitsForTenant per audit A9 (#36).
Masking is disabled (deps = nil); use GenerateSchemaWithLimitsForTenant for the production path that needs per-tenant masking.
func GenerateSchemaWithLimitsForTenant ¶
func GenerateSchemaWithLimitsForTenant(gs *storage.GraphStorage, config *LimitConfig, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
GenerateSchemaWithLimitsForTenant scopes the schema's type registry to one tenant's labels. Audit A9 (2026-05-08).
deps is the F3 masking hookup; nil disables masking. The pkg/api server passes the server's PolicyStore + Masker.
func GenerateSchemaWithMutations ¶
func GenerateSchemaWithMutations(gs *storage.GraphStorage) (graphql.Schema, error)
GenerateSchemaWithMutations generates a GraphQL schema with mutation support (tenant-blind). API callers should use GenerateSchemaWithMutationsForTenant per audit A9 (#36).
Masking is disabled (deps = nil).
func GenerateSchemaWithMutationsForTenant ¶
func GenerateSchemaWithMutationsForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)
GenerateSchemaWithMutationsForTenant scopes the schema to one tenant's labels. Audit A9.
deps is the F3 masking hookup; nil disables masking.
func GenerateSchemaWithSearch ¶
func GenerateSchemaWithSearch(gs *storage.GraphStorage, searchIndex *search.FullTextIndex, deps *MaskingDeps) (graphql.Schema, error)
GenerateSchemaWithSearch creates a GraphQL schema with full-text search.
deps is the F3 masking hookup; nil disables masking on the search-result node properties (the result type contains the matched node's full property bag JSON-encoded; masking flows through there).
func GenerateSchemaWithSorting ¶
func GenerateSchemaWithSorting(gs *storage.GraphStorage) (graphql.Schema, error)
GenerateSchemaWithSorting generates a GraphQL schema with sorting support
func GenerateSchemaWithSubscriptions ¶
func GenerateSchemaWithSubscriptions(gs *storage.GraphStorage, ps *pubsub.PubSub) (graphql.Schema, error)
GenerateSchemaWithSubscriptions creates a GraphQL schema with subscription support
func GetDepthLimit ¶
GetDepthLimit retrieves the depth limit for a schema
func PublishEdgeCreated ¶
PublishEdgeCreated publishes an edge creation event
func PublishEdgeDeleted ¶
PublishEdgeDeleted publishes an edge deletion event
func PublishEdgeUpdated ¶
PublishEdgeUpdated publishes an edge update event
func PublishNodeCreated ¶
PublishNodeCreated publishes a node creation event
func PublishNodeDeleted ¶
PublishNodeDeleted publishes a node deletion event
func PublishNodeUpdated ¶
PublishNodeUpdated publishes a node update event
func RegisterDepthLimitedSchema ¶
func RegisterDepthLimitedSchema(wrapper *DepthLimitedSchema)
RegisterDepthLimitedSchema registers a depth-limited schema
func SubscribeToEdgeCreated ¶
func SubscribeToEdgeCreated(ps *pubsub.PubSub, ctx context.Context, edgeType string) (*pubsub.Subscription, error)
SubscribeToEdgeCreated subscribes to edge creation events
func SubscribeToEdgeDeleted ¶
func SubscribeToEdgeDeleted(ps *pubsub.PubSub, ctx context.Context, edgeID uint64) (*pubsub.Subscription, error)
SubscribeToEdgeDeleted subscribes to edge deletion events for a specific edge
func SubscribeToEdgeUpdated ¶
func SubscribeToEdgeUpdated(ps *pubsub.PubSub, ctx context.Context, edgeID uint64) (*pubsub.Subscription, error)
SubscribeToEdgeUpdated subscribes to edge update events for a specific edge
func SubscribeToNodeCreated ¶
func SubscribeToNodeCreated(ps *pubsub.PubSub, ctx context.Context, label string) (*pubsub.Subscription, error)
SubscribeToNodeCreated subscribes to node creation events
func SubscribeToNodeDeleted ¶
func SubscribeToNodeDeleted(ps *pubsub.PubSub, ctx context.Context, nodeID uint64) (*pubsub.Subscription, error)
SubscribeToNodeDeleted subscribes to node deletion events for a specific node
func SubscribeToNodeUpdated ¶
func SubscribeToNodeUpdated(ps *pubsub.PubSub, ctx context.Context, nodeID uint64) (*pubsub.Subscription, error)
SubscribeToNodeUpdated subscribes to node update events for a specific node
func ValidateComplexityConfig ¶
func ValidateComplexityConfig(config *ComplexityConfig) error
ValidateComplexityConfig validates the complexity configuration
func ValidateLimitConfig ¶
func ValidateLimitConfig(config *LimitConfig) error
ValidateLimitConfig validates the limit configuration
func ValidateQueryComplexity ¶
func ValidateQueryComplexity(query string, config *ComplexityConfig, variableValues map[string]any) (int, error)
ValidateQueryComplexity validates a query against the complexity limit
func ValidateQueryDepth ¶
ValidateQueryDepth validates a query against the depth limit
Types ¶
type ComplexityConfig ¶
type ComplexityConfig struct {
MaxComplexity int // Maximum allowed complexity score
ListMultiplier int // Multiplier for list fields (default: 10)
DefaultListLimit int // Default limit for lists without explicit limit (default: 100)
}
ComplexityConfig defines configuration for query complexity analysis
type DataLoader ¶
type DataLoader struct {
// contains filtered or unexported fields
}
DataLoader batches and caches requests
func NewDataLoader ¶
func NewDataLoader(batchFn BatchFunc, config *DataLoaderConfig) *DataLoader
NewDataLoader creates a new DataLoader
func NewIncomingEdgesDataLoader ¶
func NewIncomingEdgesDataLoader(gs *storage.GraphStorage) *DataLoader
NewIncomingEdgesDataLoader creates a DataLoader for loading incoming edges
func NewNodeDataLoader ¶
func NewNodeDataLoader(gs *storage.GraphStorage) *DataLoader
NewNodeDataLoader creates a DataLoader for loading nodes by ID
func NewOutgoingEdgesDataLoader ¶
func NewOutgoingEdgesDataLoader(gs *storage.GraphStorage) *DataLoader
NewOutgoingEdgesDataLoader creates a DataLoader for loading outgoing edges
func (*DataLoader) Clear ¶
func (dl *DataLoader) Clear(key string)
Clear removes a key from the cache
func (*DataLoader) ClearAll ¶
func (dl *DataLoader) ClearAll()
ClearAll removes all keys from the cache
func (*DataLoader) Prime ¶
func (dl *DataLoader) Prime(key string, value any)
Prime adds a value to the cache without calling the batch function
type DataLoaderConfig ¶
type DataLoaderConfig struct {
BatchSize int // Maximum number of keys to batch together
Wait time.Duration // How long to wait before dispatching a batch
}
DataLoaderConfig configures a DataLoader instance
type DataLoaderContext ¶
type DataLoaderContext struct {
Nodes *DataLoader
OutgoingEdges *DataLoader
IncomingEdges *DataLoader
}
DataLoaderContext holds all DataLoaders for a request
func GenerateSchemaWithDataLoader ¶
func GenerateSchemaWithDataLoader(gs *storage.GraphStorage) (graphql.Schema, *DataLoaderContext)
GenerateSchemaWithDataLoader creates a GraphQL schema with DataLoader integration
type DepthLimitedSchema ¶
type DepthLimitedSchema struct {
// contains filtered or unexported fields
}
DepthLimitedSchema wraps a schema with automatic depth validation
type EdgeEvent ¶
type EdgeEvent struct {
Type string // "created", "updated", "deleted"
Edge *storage.Edge
EdgeType string // For filtering by edge type
}
EdgeEvent represents an edge change event
type FilterCondition ¶
FilterCondition represents a single filter condition
type FilterExpression ¶
type FilterExpression struct {
// For simple conditions
Conditions []FilterCondition
// For logical operators
AND []FilterExpression
OR []FilterExpression
NOT *FilterExpression
}
FilterExpression represents a filter that can be simple or logical
type GraphQLError ¶
type GraphQLError struct {
Message string `json:"message"`
}
GraphQLError represents a GraphQL error
type GraphQLHandler ¶
type GraphQLHandler struct {
// contains filtered or unexported fields
}
GraphQLHandler handles GraphQL HTTP requests
func NewGraphQLHandler ¶
func NewGraphQLHandler(schema graphql.Schema) *GraphQLHandler
NewGraphQLHandler creates a new GraphQL HTTP handler
func (*GraphQLHandler) ServeHTTP ¶
func (h *GraphQLHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP handles HTTP requests for GraphQL queries
type GraphQLRequest ¶
type GraphQLRequest struct {
Query string `json:"query"`
Variables map[string]any `json:"variables,omitempty"`
OperationName string `json:"operationName,omitempty"`
}
GraphQLRequest represents a GraphQL HTTP request
type GraphQLResponse ¶
type GraphQLResponse struct {
Data any `json:"data,omitempty"`
Errors []GraphQLError `json:"errors,omitempty"`
}
GraphQLResponse represents a GraphQL HTTP response
type LimitConfig ¶
type LimitConfig struct {
DefaultLimit int // Default limit when no limit specified
MaxLimit int // Maximum allowed limit
}
LimitConfig defines limits for query results
type MaskingDeps ¶
type MaskingDeps struct {
Store *masking.PolicyStore
Masker *masking.Masker
}
MaskingDeps bundles the per-tenant masking dependencies that GraphQL resolvers need to apply a tenant's masking policy at response-shaping time. The deps are server-lifecycle and captured in resolver closures (mirroring how *storage.GraphStorage is captured); the actual tenant and policy are resolved per-request via context.
A nil *MaskingDeps means masking is disabled for this schema (e.g., CLI builds, tests, or schema variants that aren't on the production API path). All resolver hook sites tolerate nil deps gracefully.
type NodeEvent ¶
type NodeEvent struct {
Type string // "created", "updated", "deleted"
Node *storage.Node
Label string // For filtering by label
}
NodeEvent represents a node change event
type OrderByInput ¶
OrderByInput represents sorting criteria
Source Files
¶
- aggregation.go
- aggregation_resolvers.go
- aggregation_types.go
- complexity.go
- dataloader.go
- dataloader_edges.go
- dataloader_nodes.go
- depth.go
- doc.go
- edges_resolvers.go
- edges_schema.go
- edges_types.go
- filtering_eval.go
- filtering_schema.go
- filtering_types.go
- http.go
- limits.go
- masking_hook.go
- mutation_type.go
- mutations.go
- mutations_resolvers.go
- mutations_types.go
- pagination_cursor.go
- pagination_resolvers.go
- pagination_schema.go
- query.go
- schema.go
- schema_dataloader.go
- schema_search.go
- sorting_core.go
- sorting_resolvers.go
- sorting_schema.go
- subscriptions.go