Documentation
¶
Overview ¶
Package graph provides a modern, secure GraphQL handler for Go with built-in authentication, validation, and an intuitive builder API.
Built on top of graphql-go (github.com/graphql-go/graphql), this package simplifies GraphQL server development with sensible defaults while maintaining full flexibility.
Features ¶
- Zero Config Start: Default hello world schema included
- Fluent Builder API: Clean, type-safe schema construction
- Built-in Authentication: Automatic Bearer token extraction
- Security First: Query depth, complexity, and introspection protection
- Response Sanitization: Remove field suggestions from errors
- Framework Agnostic: Works with net/http, Gin, Chi, or any HTTP framework
Quick Start ¶
Start immediately with the default schema:
import "github.com/paulmanoni/graph"
func main() {
handler := graph.NewHTTP(&graph.GraphContext{
Playground: true,
DEBUG: true,
})
http.Handle("/graphql", handler)
http.ListenAndServe(":8080", nil)
}
Builder Pattern ¶
Use the fluent builder API for clean schema construction:
func getUser() graph.QueryField {
return graph.NewResolver[User]("user").
WithArgs(graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{Type: graphql.String},
}).
WithResolver(func(p graphql.ResolveParams) (interface{}, error) {
id, _ := graph.GetArgString(p, "id")
return User{ID: id, Name: "Alice"}, nil
}).BuildQuery()
}
Authentication ¶
Automatic Bearer token extraction with optional user details fetching:
handler := graph.NewHTTP(&graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{
QueryFields: []graph.QueryField{getProtectedQuery()},
},
UserDetailsFn: func(ctx context.Context, token string) (context.Context, interface{}, error) {
user, err := validateAndGetUser(token)
if err != nil {
return ctx, nil, err
}
// Add values to context accessible via p.Context.Value() in resolvers
ctx = context.WithValue(ctx, "userID", user.ID)
return ctx, user, nil
},
})
Access token in resolvers:
func getProtectedQuery() graph.QueryField {
return graph.NewResolver[User]("me").
WithResolver(func(p graphql.ResolveParams) (interface{}, error) {
token, err := graph.GetRootString(p, "token")
if err != nil {
return nil, fmt.Errorf("authentication required")
}
// Use token...
}).BuildQuery()
}
Security ¶
Enable security features for production:
handler := graph.NewHTTP(&graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{...},
DEBUG: false, // Enable security features
EnableValidation: true, // Max depth: 10, Max aliases: 4, Max complexity: 200
EnableSanitization: true, // Remove field suggestions from errors
Playground: false, // Disable playground in production
})
Helper Functions ¶
Extract arguments safely:
name, err := graph.GetArgString(p, "name") age, err := graph.GetArgInt(p, "age") active, err := graph.GetArgBool(p, "active")
Access root values:
token, err := graph.GetRootString(p, "token") var user User err := graph.GetRootInfo(p, "details", &user)
For more information, see https://github.com/paulmanoni/graph
Index ¶
- Constants
- Variables
- func AsyncFieldResolver(resolver graphql.FieldResolveFn) graphql.FieldResolveFn
- func CachedFieldResolver(cacheKey func(graphql.ResolveParams) string, resolver graphql.FieldResolveFn) graphql.FieldResolveFn
- func ConditionalResolver(condition func(graphql.ResolveParams) bool, ...) graphql.FieldResolveFn
- func DataTransformResolver(transform func(interface{}) interface{}) graphql.FieldResolveFn
- func ExecuteValidationRules(queryString string, schema *graphql.Schema, rules []ValidationRule, ...) error
- func ExtractBearerToken(r *http.Request) string
- func GenerateArgsFromStruct[T any]() graphql.FieldConfigArgument
- func GenerateGraphQLFields[T any]() graphql.Fields
- func GenerateGraphQLObject[T any](name string) *graphql.Object
- func GenerateInputObject[T any](name string) *graphql.InputObject
- func Get[T any](a ArgsGetter, name string) T
- func GetArg(p ResolveParams, key string, target interface{}) error
- func GetArgBool(p ResolveParams, key string) (bool, error)
- func GetArgInt(p ResolveParams, key string) (int, error)
- func GetArgString(p ResolveParams, key string) (string, error)
- func GetE[T any](a ArgsGetter, name string) (T, error)
- func GetOr[T any](a ArgsGetter, name string, defaultVal T) T
- func GetRoot[T any](r RootInfoGetter, name string) T
- func GetRootE[T any](r RootInfoGetter, name string) (T, error)
- func GetRootInfo(p ResolveParams, key string, target interface{}) error
- func GetRootOr[T any](r RootInfoGetter, name string, defaultVal T) T
- func GetRootString(p ResolveParams, key string) (string, error)
- func GetTypeName[T any]() string
- func LazyFieldResolver(fieldName string, loader func(interface{}) (interface{}, error)) graphql.FieldResolveFn
- func MergeRoleConfigs(configs ...map[string][]string) map[string][]string
- func MustGet[T any](a ArgsGetter, name string) T
- func MustGetRoot[T any](r RootInfoGetter, name string) T
- func New(graphCtx GraphContext) (*handler.Handler, error)
- func NewHTTP(graphCtx *GraphContext) http.HandlerFunc
- func NewWebSocketHandler(params WebSocketParams) http.HandlerFunc
- func PerUserBudgetFunc(budgets map[string]int, defaultBudget int) func(string) (int, error)
- func RegisterObjectType(name string, typeFactory func() *graphql.Object) *graphql.Object
- func SimpleBudgetFunc(budget int) func(string) (int, error)
- func UnmarshalSubscriptionMessage[T any](msg *Message) (*T, error)
- func ValidateGraphQLQuery(queryString string, schema *graphql.Schema) error
- type ASTVisitor
- type ActionBuilder
- type ArgInfo
- type ArgValidator
- type Args
- type ArgsGetter
- type ArgsMap
- type BaseRule
- func (r *BaseRule) Disable()
- func (r *BaseRule) Enable()
- func (r *BaseRule) Enabled() bool
- func (r *BaseRule) Name() string
- func (r *BaseRule) NewError(message string) *ValidationError
- func (r *BaseRule) NewErrorf(format string, args ...interface{}) *ValidationError
- func (r *BaseRule) SetEnabled(enabled bool)
- type BlockedFieldsRule
- type Connection
- type CreateBuilder
- type DeleteBuilder
- type EchoInput
- type ErrorCode
- type FieldConfig
- type FieldGenerator
- type FieldInfo
- type FieldKind
- type FieldMiddleware
- type FieldResolveFn
- type GenericTypeInfo
- type GraphContext
- type HasIDInterface
- type HasPermissionsInterface
- type HasRolesInterface
- type InMemoryPubSub
- func (p *InMemoryPubSub) Close() error
- func (p *InMemoryPubSub) Publish(ctx context.Context, topic string, data interface{}) error
- func (p *InMemoryPubSub) Subscribe(ctx context.Context, topic string) <-chan *Message
- func (p *InMemoryPubSub) Unsubscribe(ctx context.Context, subscriptionID string) error
- type InputAuthorizer
- type InputNormalizer
- type InputObjectInfo
- type InputValidator
- type Introspectable
- type JSONTime
- type MaxAliasesRule
- type MaxComplexityRule
- type MaxDepthRule
- type MaxTokensRule
- type Message
- type MiddlewareInfo
- type MultiValidationError
- type MutationBuilder
- func (b *MutationBuilder[T, In]) Action() *ActionBuilder[T, In]
- func (b *MutationBuilder[T, In]) Create() *CreateBuilder[T, In]
- func (b *MutationBuilder[T, In]) Delete() *DeleteBuilder[T, In]
- func (b *MutationBuilder[T, In]) Update() *UpdateBuilder[T, In]
- func (b *MutationBuilder[T, In]) Upsert() *UpsertBuilder[T, In]
- func (b *MutationBuilder[T, In]) Use(mw ...FieldMiddleware) *MutationBuilder[T, In]
- func (b *MutationBuilder[T, In]) WithDescription(d string) *MutationBuilder[T, In]
- func (b *MutationBuilder[T, In]) WithInputName(n string) *MutationBuilder[T, In]
- type MutationError
- type MutationField
- type NoIntrospectionRule
- type PageInfo
- type PaginatedResponse
- type PaginationArgs
- type Patch
- type PatchInputValidator
- type PermissionRule
- type PermissionRules
- type PresenceSet
- type PubSub
- type QueryASTCache
- type QueryField
- type RateLimitOption
- type RateLimitRule
- type RequireAuthRule
- type ResolveParams
- type Result
- type RoleRule
- type RoleRules
- type RootInfo
- type RootInfoGetter
- type SchemaBuilder
- type SchemaBuilderParams
- type SubscriptionField
- type SubscriptionFilterFn
- type SubscriptionResolveFn
- type SubscriptionResolver
- func (s *SubscriptionResolver[T]) BuildSubscription() SubscriptionField
- func (s *SubscriptionResolver[T]) WithArgs(args graphql.FieldConfigArgument) *SubscriptionResolver[T]
- func (s *SubscriptionResolver[T]) WithDescription(desc string) *SubscriptionResolver[T]
- func (s *SubscriptionResolver[T]) WithFieldMiddleware(fieldName string, middleware FieldMiddleware) *SubscriptionResolver[T]
- func (s *SubscriptionResolver[T]) WithFieldResolver(fieldName string, resolver graphql.FieldResolveFn) *SubscriptionResolver[T]
- func (s *SubscriptionResolver[T]) WithFilter(fn SubscriptionFilterFn[T]) *SubscriptionResolver[T]
- func (s *SubscriptionResolver[T]) WithMiddleware(middleware FieldMiddleware) *SubscriptionResolver[T]
- func (s *SubscriptionResolver[T]) WithResolver(fn SubscriptionResolveFn[T]) *SubscriptionResolver[T]
- type UnifiedResolver
- func (r *UnifiedResolver[T]) AsList() *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) AsMutation() *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) AsPaginated() *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) Build() interface{}
- func (r *UnifiedResolver[T]) BuildMutation() MutationField
- func (r *UnifiedResolver[T]) BuildQuery() QueryField
- func (r *UnifiedResolver[T]) FieldInfo() FieldInfo
- func (r *UnifiedResolver[T]) GetArgsDefinition() graphql.FieldConfigArgument
- func (r *UnifiedResolver[T]) GetMiddlewareCount() int
- func (r *UnifiedResolver[T]) GetMiddlewareInfos() []MiddlewareInfo
- func (r *UnifiedResolver[T]) IsDeprecated() bool
- func (r *UnifiedResolver[T]) IsList() bool
- func (r *UnifiedResolver[T]) IsMutation() bool
- func (r *UnifiedResolver[T]) IsPaginated() bool
- func (r *UnifiedResolver[T]) Name() string
- func (r *UnifiedResolver[T]) Serve() *graphql.Field
- func (r *UnifiedResolver[T]) WithArg(name string, argType interface{}) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithArgDefault(name string, argType interface{}, defaultValue interface{}) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithArgRequired(name string, argType interface{}) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithArgValidator(argName string, validators ...Validator) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithArgs(args graphql.FieldConfigArgument) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithArgsFromStruct(structType interface{}) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithAsyncField(fieldName string, resolver graphql.FieldResolveFn) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithCachedField(fieldName string, cacheKeyFunc func(graphql.ResolveParams) string, ...) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithComputedField(name string, fieldType graphql.Output, resolver graphql.FieldResolveFn) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithCustomField(name string, field *graphql.Field) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithDeprecated(reason string) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithDescription(desc string) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithFieldMiddleware(fieldName string, middleware FieldMiddleware) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithFieldResolver(fieldName string, resolver graphql.FieldResolveFn) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithFieldResolvers(overrides map[string]graphql.FieldResolveFn) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithInputObject(inputType interface{}) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithInputObjectFieldName(name string) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithInputObjectNullable() *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithLazyField(fieldName string, loader func(interface{}) (interface{}, error)) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithMiddleware(middleware FieldMiddleware) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithNamedMiddleware(name, description string, middleware FieldMiddleware) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithPermission(middleware FieldMiddleware) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithResolver(resolver func(ResolveParams) (*T, error)) *UnifiedResolver[T]
- func (r *UnifiedResolver[T]) WithTypedResolver(typedResolver interface{}) *UnifiedResolver[T]
- type UpdateBuilder
- type UpsertBuilder
- type ValidationContext
- type ValidationError
- type ValidationOptions
- type ValidationRule
- func CombineRules(ruleSets ...[]ValidationRule) []ValidationRule
- func DefaultValidationRules() []ValidationRule
- func DevelopmentValidationRules() []ValidationRule
- func NewBlockedFieldsRule(fields ...string) ValidationRule
- func NewMaxAliasesRule(maxAliases int) ValidationRule
- func NewMaxComplexityRule(maxComplexity int) ValidationRule
- func NewMaxDepthRule(maxDepth int) ValidationRule
- func NewMaxTokensRule(maxTokens int) ValidationRule
- func NewNoIntrospectionRule() ValidationRule
- func NewPermissionRule(field string, permissions ...string) ValidationRule
- func NewPermissionRules(config map[string][]string) ValidationRule
- func NewRateLimitRule(opts ...RateLimitOption) ValidationRule
- func NewRequireAuthRule(targets ...string) ValidationRule
- func NewRoleRule(field string, roles ...string) ValidationRule
- func NewRoleRules(config map[string][]string) ValidationRule
- func ProductionValidationRules() []ValidationRule
- type Validator
- type ValidatorInfo
- type WSMessage
- type WebSocketManager
- type WebSocketParams
Constants ¶
const ( // Client -> Server (graphql-ws) MessageTypeConnectionInit = "connection_init" MessageTypeSubscribe = "subscribe" MessageTypePing = "ping" // Client -> Server (subscriptions-transport-ws - legacy) MessageTypeStart = "start" // Legacy equivalent of "subscribe" MessageTypeStop = "stop" // Legacy equivalent of "complete" // Server -> Client (graphql-ws) MessageTypeConnectionAck = "connection_ack" MessageTypeNext = "next" MessageTypeError = "error" MessageTypeComplete = "complete" MessageTypePong = "pong" // Server -> Client (subscriptions-transport-ws - legacy) MessageTypeData = "data" // Legacy equivalent of "next" MessageTypeConnectionError = "connection_error" // Legacy connection error MessageTypeConnectionKeepAlive = "ka" // Legacy keep-alive )
GraphQL WebSocket Protocol message types Supports both graphql-ws (new) and subscriptions-transport-ws (legacy) protocols
const SpringShortLayout = "2006-01-02T15:04"
SpringShortLayout is the time format used by Spring Boot for DateTime serialization. Format: yyyy-MM-dd'T'HH:mm (e.g., "2024-01-15T14:30")
Variables ¶
var ( String = graphql.String Int = graphql.Int Float = graphql.Float Boolean = graphql.Boolean ID = graphql.ID )
GraphQL scalar type constants for use with WithArg These mirror Go's type names for intuitive usage:
WithArg("id", graph.String) // like using 'string'
WithArg("limit", graph.Int) // like using 'int'
var ( // SecurityRules provides standard security validation // - Max depth: 10 // - Max complexity: 200 // - Max aliases: 4 // - No introspection SecurityRules = []ValidationRule{ NewMaxDepthRule(10), NewMaxComplexityRule(200), NewMaxAliasesRule(4), NewNoIntrospectionRule(), } // StrictSecurityRules provides strict security for production // - Max depth: 8 // - Max complexity: 150 // - Max aliases: 3 // - Max tokens: 500 // - No introspection StrictSecurityRules = []ValidationRule{ NewMaxDepthRule(8), NewMaxComplexityRule(150), NewMaxAliasesRule(3), NewMaxTokensRule(500), NewNoIntrospectionRule(), } // DevelopmentRules provides lenient rules for development // - Max depth: 20 // - Max complexity: 500 DevelopmentRules = []ValidationRule{ NewMaxDepthRule(20), NewMaxComplexityRule(500), } )
var ( // AdminOnlyFields - fields that require admin role AdminOnlyFields = map[string][]string{ "deleteUser": {"admin"}, "deleteAccount": {"admin"}, "viewAuditLog": {"admin"}, "systemSettings": {"admin"}, "manageRoles": {"admin"}, } // ManagerFields - fields that require admin or manager role ManagerFields = map[string][]string{ "approveOrder": {"admin", "manager"}, "viewReports": {"admin", "manager"}, "manageTeam": {"admin", "manager"}, "bulkOperations": {"admin", "manager"}, } // AuditorFields - fields that require admin or auditor role AuditorFields = map[string][]string{ "viewAuditLog": {"admin", "auditor"}, "exportLogs": {"admin", "auditor"}, "viewAnalytics": {"admin", "auditor"}, } )
Common role configurations for convenience
var ( ErrPubSubClosed = newError("pubsub is closed") ErrSubscriptionNotFound = newError("subscription not found") )
Common errors
var DateTime = graphql.NewScalar(graphql.ScalarConfig{ Name: "DateTime", Description: "The `DateTime` scalar type formatted as yyyy-MM-dd'T'HH:mm", Serialize: serializeDateTime, ParseValue: unserializeDateTime, ParseLiteral: func(valueAST ast.Value) interface{} { if v, ok := valueAST.(*ast.StringValue); ok { return unserializeDateTime(v.Value) } return nil }, })
DateTime is a GraphQL scalar type for date-time values. It uses the Spring Boot date format: yyyy-MM-dd'T'HH:mm (e.g., "2024-01-15T14:30"). All times are automatically converted to UTC.
Usage in struct fields:
type Event struct {
Name string `json:"name"`
StartTime time.Time `json:"startTime"` // Will use DateTime scalar
}
The scalar automatically handles:
- Serialization: time.Time → "2024-01-15T14:30"
- Deserialization: "2024-01-15T14:30" → time.Time
- UTC conversion for all values
Functions ¶
func AsyncFieldResolver ¶
func AsyncFieldResolver(resolver graphql.FieldResolveFn) graphql.FieldResolveFn
AsyncFieldResolver executes a resolver asynchronously
func CachedFieldResolver ¶
func CachedFieldResolver(cacheKey func(graphql.ResolveParams) string, resolver graphql.FieldResolveFn) graphql.FieldResolveFn
CachedFieldResolver caches field results with a key function
func ConditionalResolver ¶
func ConditionalResolver(condition func(graphql.ResolveParams) bool, ifTrue, ifFalse graphql.FieldResolveFn) graphql.FieldResolveFn
ConditionalResolver resolves based on a condition
func DataTransformResolver ¶
func DataTransformResolver(transform func(interface{}) interface{}) graphql.FieldResolveFn
DataTransformResolver applies a transformation to a field value
func ExecuteValidationRules ¶ added in v1.1.1
func ExecuteValidationRules( queryString string, schema *graphql.Schema, rules []ValidationRule, userDetails interface{}, options *ValidationOptions, ) error
ExecuteValidationRules executes a set of validation rules against a GraphQL query. This is the modern validation system that supports custom rules.
Parameters:
- queryString: The GraphQL query to validate
- schema: The GraphQL schema
- rules: The validation rules to execute
- authCtx: Authentication context (can be nil if not needed)
- options: Validation options (can be nil for defaults)
Returns:
- nil if validation passes
- *ValidationError for single rule failure
- *MultiValidationError for multiple rule failures
Example:
rules := []ValidationRule{
NewMaxDepthRule(10),
NewRequireAuthRule("mutation"),
NewRoleRules(AdminOnlyFields),
}
err := ExecuteValidationRules(query, schema, rules, authCtx, nil)
func ExtractBearerToken ¶
ExtractBearerToken extracts the Bearer token from the Authorization header. It performs case-insensitive matching for the "Bearer " prefix and trims whitespace.
Returns an empty string if:
- The Authorization header is missing
- The header doesn't start with "Bearer " (case-insensitive)
- The token value is empty
Example:
// Authorization: Bearer abc123xyz token := graph.ExtractBearerToken(r) // Returns: "abc123xyz"
func GenerateArgsFromStruct ¶
func GenerateArgsFromStruct[T any]() graphql.FieldConfigArgument
func GenerateGraphQLFields ¶
func GenerateInputObject ¶
func GenerateInputObject[T any](name string) *graphql.InputObject
func Get ¶ added in v1.1.7
func Get[T any](a ArgsGetter, name string) T
Get retrieves an argument by name with type safety. Returns zero value if key is missing or conversion fails. Use GetE for explicit error handling.
Works with both graph.Args (from WithArg) and graph.ArgsMap (from p.Args):
// With graph.Args (from WithArg chainable API) id := graph.Get[string](args, "id") // With p.Args (from graphql.FieldConfigArgument) channelID := graph.Get[string](graph.ArgsMap(p.Args), "channelID")
func GetArg ¶
func GetArg(p ResolveParams, key string, target interface{}) error
GetArg safely extracts a value from p.Args and unmarshals it into the target. This is useful for extracting complex types like structs, slices, or maps.
The function handles:
- Primitive types (string, int, bool) with optimized direct assignment
- Complex types using JSON marshaling/unmarshaling for type conversion
- Type mismatches with descriptive error messages
Returns an error if:
- The argument key doesn't exist
- Type conversion fails
Example:
var input CreateUserInput
if err := graph.GetArg(p, "input", &input); err != nil {
return nil, err
}
// Use input.Name, input.Email, etc.
func GetArgBool ¶
func GetArgBool(p ResolveParams, key string) (bool, error)
GetArgBool safely extracts a bool argument from p.Args. Returns an error if the argument doesn't exist or is not a boolean.
Example:
active, err := graph.GetArgBool(p, "active")
func GetArgInt ¶
func GetArgInt(p ResolveParams, key string) (int, error)
GetArgInt safely extracts an int argument from p.Args. Handles both int and float64 types (JSON numbers are parsed as float64). Returns an error if the argument doesn't exist or is not a number.
Example:
age, err := graph.GetArgInt(p, "age")
func GetArgString ¶
func GetArgString(p ResolveParams, key string) (string, error)
GetArgString safely extracts a string argument from p.Args. Returns an error if the argument doesn't exist or is not a string.
Example:
name, err := graph.GetArgString(p, "name")
func GetE ¶ added in v1.1.8
func GetE[T any](a ArgsGetter, name string) (T, error)
GetE retrieves an argument by name with type safety and error handling. Returns an error if the key is missing or type conversion fails.
Works with both graph.Args and graph.ArgsMap (p.Args):
// With graph.Args input, err := graph.GetE[UserInput](args, "input") // With p.Args channelID, err := graph.GetE[string](graph.ArgsMap(p.Args), "channelID")
func GetOr ¶ added in v1.1.7
func GetOr[T any](a ArgsGetter, name string, defaultVal T) T
GetOr retrieves an argument by name with a default value if not found.
Works with both graph.Args and graph.ArgsMap (p.Args):
// With graph.Args limit := graph.GetOr[int](args, "limit", 10) // With p.Args limit := graph.GetOr[int](graph.ArgsMap(p.Args), "limit", 10)
func GetRoot ¶ added in v1.2.2
func GetRoot[T any](r RootInfoGetter, name string) T
GetRoot retrieves a value from root info by name with type safety. Returns zero value if key is missing or conversion fails. Use GetRootE for explicit error handling.
Usage:
user := graph.GetRoot[UserDetails](graph.NewRootInfo(p), "details") token := graph.GetRoot[string](graph.NewRootInfo(p), "token")
func GetRootE ¶ added in v1.2.2
func GetRootE[T any](r RootInfoGetter, name string) (T, error)
GetRootE retrieves a value from root info by name with type safety and error handling. Returns an error if the key is missing or type conversion fails.
Usage:
user, err := graph.GetRootE[UserDetails](graph.NewRootInfo(p), "details")
if err != nil {
return nil, fmt.Errorf("authentication required: %w", err)
}
func GetRootInfo ¶
func GetRootInfo(p ResolveParams, key string, target interface{}) error
GetRootInfo safely extracts a value from p.Info.RootValue and unmarshals it into the target. This is commonly used to retrieve user details set by UserDetailsFn in the GraphContext.
The function handles:
- Primitive types (string, int) with optimized direct assignment
- Complex types using JSON marshaling/unmarshaling for type conversion
- Type mismatches with descriptive error messages
Returns an error if:
- Root value is nil or not a map
- The key doesn't exist in the root value
- Type conversion fails
Example:
// In your resolver
var user UserDetails
if err := graph.GetRootInfo(p, "details", &user); err != nil {
return nil, fmt.Errorf("authentication required")
}
// Use user.ID, user.Email, etc.
func GetRootOr ¶ added in v1.2.2
func GetRootOr[T any](r RootInfoGetter, name string, defaultVal T) T
GetRootOr retrieves a value from root info by name with a default value if not found.
Usage:
token := graph.GetRootOr[string](graph.NewRootInfo(p), "token", "anonymous") userID := graph.GetRootOr[int](graph.NewRootInfo(p), "userID", 0)
func GetRootString ¶
func GetRootString(p ResolveParams, key string) (string, error)
GetRootString safely extracts a string value from p.Info.RootValue. This is commonly used to retrieve the authentication token.
Returns an error if:
- Root value is nil or not a map
- The key doesn't exist in the root value
- The value is not a string
Example:
// Get authentication token
token, err := graph.GetRootString(p, "token")
if err != nil {
return nil, fmt.Errorf("authentication required")
}
// Validate token...
func GetTypeName ¶
func LazyFieldResolver ¶
func LazyFieldResolver(fieldName string, loader func(interface{}) (interface{}, error)) graphql.FieldResolveFn
LazyFieldResolver loads a field only when requested
func MergeRoleConfigs ¶ added in v1.1.1
MergeRoleConfigs combines multiple role configurations
Example:
allRoles := MergeRoleConfigs(AdminOnlyFields, ManagerFields, AuditorFields)
func MustGet ¶ added in v1.1.8
func MustGet[T any](a ArgsGetter, name string) T
MustGet retrieves an argument by name with type safety. Panics if the key is missing or conversion fails. Use only when you're certain the argument exists and is valid.
Works with both graph.Args and graph.ArgsMap (p.Args).
func MustGetRoot ¶ added in v1.2.2
func MustGetRoot[T any](r RootInfoGetter, name string) T
MustGetRoot retrieves a value from root info by name with type safety. Panics if the key is missing or conversion fails. Use only when you're certain the value exists and is valid.
Usage:
user := graph.MustGetRoot[UserDetails](graph.NewRootInfo(p), "details")
func New ¶
func New(graphCtx GraphContext) (*handler.Handler, error)
New creates a GraphQL handler from the provided GraphContext. It builds the schema and sets up authentication with token extraction and user details.
The handler automatically:
- Extracts tokens using TokenExtractorFn (defaults to Bearer token extraction)
- Fetches user details using UserDetailsFn if provided
- Adds token and details to the root value for access in resolvers
Returns an error if schema building fails.
Example:
handler, err := graph.New(graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{
QueryFields: []graph.QueryField{getUserQuery()},
},
Playground: true,
})
func NewHTTP ¶
func NewHTTP(graphCtx *GraphContext) http.HandlerFunc
NewHTTP creates a standard http.HandlerFunc with built-in validation and sanitization support. This is the recommended way to create a GraphQL handler for production use.
The handler automatically detects WebSocket upgrade requests and handles them appropriately when subscriptions are enabled (EnableSubscriptions: true).
The handler is fully compatible with net/http and any HTTP framework (Gin, Chi, Echo, etc.). If graphCtx is nil, defaults to DEBUG mode with Playground enabled.
Behavior:
- In DEBUG mode (DEBUG: true): Skips all validation and sanitization for easier development
- In production (DEBUG: false): Enables validation and sanitization based on configuration
- Panics during initialization if schema building fails (fail-fast approach)
- WebSocket upgrade requests are handled when EnableSubscriptions: true
Security Features (when DEBUG: false):
- EnableValidation: Validates query depth (max 10), aliases (max 4), complexity (max 200), and blocks introspection
- EnableSanitization: Removes field suggestions from error messages to prevent information disclosure
Example without subscriptions:
// Development setup
handler := graph.NewHTTP(&graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{
QueryFields: []graph.QueryField{getUserQuery()},
},
DEBUG: true,
Playground: true,
})
// Production setup
handler := graph.NewHTTP(&graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{...},
DEBUG: false,
EnableValidation: true,
EnableSanitization: true,
Playground: false,
UserDetailsFn: func(ctx context.Context, token string) (context.Context, interface{}, error) {
user, err := validateToken(token)
if err != nil {
return ctx, nil, err
}
// Add user ID to context for access in resolvers via p.Context.Value("userID")
ctx = context.WithValue(ctx, "userID", user.ID)
return ctx, user, nil
},
})
http.Handle("/graphql", handler)
http.ListenAndServe(":8080", nil)
Example with subscriptions:
pubsub := graph.NewInMemoryPubSub()
defer pubsub.Close()
handler := graph.NewHTTP(&graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{
QueryFields: []graph.QueryField{getUserQuery()},
MutationFields: []graph.MutationField{createUserMutation()},
SubscriptionFields: []graph.SubscriptionField{userSubscription(pubsub)},
},
PubSub: pubsub,
EnableSubscriptions: true,
DEBUG: false,
})
http.Handle("/graphql", handler)
http.ListenAndServe(":8080", nil)
func NewWebSocketHandler ¶ added in v1.1.0
func NewWebSocketHandler(params WebSocketParams) http.HandlerFunc
NewWebSocketHandler creates an HTTP handler for WebSocket connections. This handler upgrades HTTP connections to WebSocket and manages GraphQL subscriptions.
Example:
params := graph.WebSocketParams{
Schema: schema,
PubSub: pubsub,
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("Origin")
return origin == "https://example.com"
},
AuthFn: func(r *http.Request) (interface{}, error) {
token := ExtractBearerToken(r)
return validateToken(token)
},
}
http.Handle("/graphql", graph.NewWebSocketHandler(params))
func PerUserBudgetFunc ¶ added in v1.1.1
PerUserBudgetFunc creates a budget function with per-user budgets Useful for different tier users or testing
func RegisterObjectType ¶
RegisterObjectType registers a GraphQL object type in the global registry Returns existing type if already registered, otherwise creates and registers new type
func SimpleBudgetFunc ¶ added in v1.1.1
SimpleBudgetFunc creates a simple budget function that returns a fixed budget Useful for testing or simple rate limiting scenarios
func UnmarshalSubscriptionMessage ¶ added in v1.1.0
Helper function to unmarshal subscription messages
func ValidateGraphQLQuery ¶
ValidateGraphQLQuery validates a GraphQL query against security rules. This function implements multiple layers of protection against malicious or expensive queries.
Validation Rules:
- Max Query Depth: 10 levels (prevents deeply nested queries)
- Max Aliases: 4 per query (prevents alias-based DoS attacks)
- Max Complexity: 200 (prevents computationally expensive queries)
- Introspection: Blocked (__schema and __type queries are rejected)
Returns an error if:
- Query depth exceeds 10 levels
- Query contains more than 4 aliases
- Query complexity exceeds 200
- Query contains __schema or __type introspection fields
- Query parsing fails (though parsing errors are allowed to pass through)
Example usage:
if err := graph.ValidateGraphQLQuery(queryString, schema); err != nil {
// Reject query with HTTP 400
return fmt.Errorf("invalid query: %w", err)
}
// Query is safe to execute
Enable this in production with GraphContext.EnableValidation = true.
Types ¶
type ASTVisitor ¶ added in v1.1.1
type ASTVisitor struct {
EnterField func(field *ast.Field, ctx *ValidationContext) error
LeaveField func(field *ast.Field, ctx *ValidationContext) error
EnterOperation func(op *ast.OperationDefinition, ctx *ValidationContext) error
LeaveOperation func(op *ast.OperationDefinition, ctx *ValidationContext) error
EnterFragment func(frag *ast.FragmentDefinition, ctx *ValidationContext) error
LeaveFragment func(frag *ast.FragmentDefinition, ctx *ValidationContext) error
}
ASTVisitor allows traversing the AST with hooks
type ActionBuilder ¶ added in v1.2.3
func (*ActionBuilder[T, In]) Build ¶ added in v1.2.3
func (b *ActionBuilder[T, In]) Build() MutationField
func (*ActionBuilder[T, In]) WithResolver ¶ added in v1.2.3
func (b *ActionBuilder[T, In]) WithResolver( fn func(ctx context.Context, in In) (*T, error), ) *ActionBuilder[T, In]
type ArgInfo ¶ added in v1.2.4
type ArgInfo struct {
Name string `json:"name"`
Type string `json:"type"` // SDL-style
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
DefaultValue any `json:"defaultValue,omitempty"`
Validators []ValidatorInfo `json:"validators,omitempty"`
}
ArgInfo describes one argument on a resolver. Required is true iff the underlying GraphQL type is NonNull. DefaultValue reflects whatever was supplied to WithArgDefault or struct-tag defaults; it's nil otherwise.
type ArgValidator ¶ added in v1.2.4
ArgValidator is the runtime shape of a validator. Return nil when the value is acceptable; return an error whose message is user-facing.
type Args ¶ added in v1.1.7
type Args struct {
// contains filtered or unexported fields
}
Args provides type-safe access to GraphQL arguments
type ArgsGetter ¶ added in v1.1.9
ArgsGetter is an interface for types that can provide arguments by name. This allows Get[T], GetE[T], GetOr[T], and MustGet[T] to work with both:
- graph.Args (from WithArg chainable API)
- graph.ArgsMap (from graphql.FieldConfigArgument / p.Args)
type ArgsMap ¶ added in v1.1.9
type ArgsMap map[string]interface{}
ArgsMap wraps map[string]interface{} to implement ArgsGetter. This allows using p.Args directly with Get[T], GetE[T], etc.
Usage:
// In subscription resolvers with graphql.FieldConfigArgument channelID := graph.Get[string](graph.ArgsMap(p.Args), "channelID")
type BaseRule ¶ added in v1.1.1
type BaseRule struct {
// contains filtered or unexported fields
}
BaseRule provides common functionality for all validation rules All custom rules should embed this struct
func NewBaseRule ¶ added in v1.1.1
NewBaseRule creates a new base rule with the given name
func (*BaseRule) NewError ¶ added in v1.1.1
func (r *BaseRule) NewError(message string) *ValidationError
NewValidationError creates a validation error for this rule
func (*BaseRule) NewErrorf ¶ added in v1.1.1
func (r *BaseRule) NewErrorf(format string, args ...interface{}) *ValidationError
NewErrorf creates a validation error with formatted message
func (*BaseRule) SetEnabled ¶ added in v1.1.1
SetEnabled sets the enabled state
type BlockedFieldsRule ¶ added in v1.1.1
type BlockedFieldsRule struct {
BaseRule
// contains filtered or unexported fields
}
BlockedFieldsRule blocks specific fields from being queried
func (*BlockedFieldsRule) BlockField ¶ added in v1.1.1
func (r *BlockedFieldsRule) BlockField(field string, reason string) *BlockedFieldsRule
BlockField adds a field to the blocked list with an optional reason
func (*BlockedFieldsRule) Validate ¶ added in v1.1.1
func (r *BlockedFieldsRule) Validate(ctx *ValidationContext) error
type Connection ¶ added in v1.1.0
type Connection struct {
// contains filtered or unexported fields
}
Connection represents a single WebSocket connection.
type CreateBuilder ¶ added in v1.2.3
func (*CreateBuilder[T, In]) Build ¶ added in v1.2.3
func (b *CreateBuilder[T, In]) Build() MutationField
func (*CreateBuilder[T, In]) WithResolver ¶ added in v1.2.3
func (b *CreateBuilder[T, In]) WithResolver( fn func(ctx context.Context, in In) (*T, error), ) *CreateBuilder[T, In]
type DeleteBuilder ¶ added in v1.2.3
func (*DeleteBuilder[T, In]) Build ¶ added in v1.2.3
func (b *DeleteBuilder[T, In]) Build() MutationField
func (*DeleteBuilder[T, In]) WithResolver ¶ added in v1.2.3
func (b *DeleteBuilder[T, In]) WithResolver( fn func(ctx context.Context, in In) (*T, error), ) *DeleteBuilder[T, In]
type EchoInput ¶ added in v1.2.3
type EchoInput struct {
Message string `json:"message" graphql:"message,required" description:"Message to echo back"`
}
EchoInput is the input for the default echo mutation.
type FieldConfig ¶
type FieldConfig struct {
Resolver graphql.FieldResolveFn
Description string
Args graphql.FieldConfigArgument
DeprecationReason string
}
type FieldGenerator ¶
type FieldGenerator[T any] struct { // contains filtered or unexported fields }
func NewFieldGenerator ¶
func NewFieldGenerator[T any]() *FieldGenerator[T]
type FieldInfo ¶ added in v1.2.4
type FieldInfo struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Kind FieldKind `json:"kind"`
ReturnType string `json:"returnType"` // SDL-style, e.g. "User!" or "[Advert]"
List bool `json:"list,omitempty"`
Paginated bool `json:"paginated,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
DeprecationReason string `json:"deprecationReason,omitempty"`
Args []ArgInfo `json:"args,omitempty"`
Middlewares []MiddlewareInfo `json:"middlewares,omitempty"`
InputObject *InputObjectInfo `json:"inputObject,omitempty"`
}
FieldInfo is a serializable snapshot of a resolver's declared metadata. It captures everything a tool would need to render documentation or a dashboard view of the field — without calling Serve() or parsing SDL.
func Inspect ¶ added in v1.2.4
Inspect returns the FieldInfo for any value implementing Introspectable, or a zero FieldInfo and false otherwise. Use this when you hold a generic QueryField / MutationField interface reference rather than the concrete *UnifiedResolver — it saves the type assertion boilerplate.
type FieldKind ¶ added in v1.2.4
type FieldKind string
FieldKind distinguishes query / mutation / subscription at the metadata level.
type FieldMiddleware ¶
type FieldMiddleware func(next FieldResolveFn) FieldResolveFn
FieldMiddleware wraps a field resolver with additional functionality (auth, logging, caching, etc.)
func AuthMiddleware ¶
func AuthMiddleware(requiredRole string) FieldMiddleware
AuthMiddleware requires a specific user role
func CacheMiddleware ¶
func CacheMiddleware(cacheKey func(ResolveParams) string) FieldMiddleware
CacheMiddleware caches field results based on a key function
type FieldResolveFn ¶
type FieldResolveFn func(p ResolveParams) (interface{}, error)
func LoggingMiddleware ¶
func LoggingMiddleware(next FieldResolveFn) FieldResolveFn
LoggingMiddleware logs field resolution time
type GenericTypeInfo ¶
type GenericTypeInfo struct {
IsGeneric bool
IsWrapper bool
BaseTypeName string
ElementType reflect.Type
WrapperFields map[string]reflect.Type
}
GenericTypeInfo holds information about a generic type
type GraphContext ¶
type GraphContext struct {
// Schema: Provide either Schema OR SchemaParams (not both)
// If both are nil, a default "hello world" schema will be created
Schema *graphql.Schema
// SchemaParams: Alternative to Schema - will be built automatically
// If nil and Schema is also nil, defaults to hello world query/mutation
SchemaParams *SchemaBuilderParams
// PubSub: PubSub system for subscriptions (optional, only needed for subscriptions)
// Use NewInMemoryPubSub() for development or RedisPubSub for production
PubSub PubSub
// EnableSubscriptions: Enable WebSocket support for GraphQL subscriptions
// Default: false (subscriptions disabled)
// Requires PubSub to be configured
EnableSubscriptions bool
// WebSocketPath: Path for WebSocket endpoint (default: same as HTTP endpoint)
// If not set, WebSocket connections will be handled on the same path as HTTP
WebSocketPath string
// WebSocketCheckOrigin: Custom function to check WebSocket upgrade origin
// If not provided, all origins are allowed (only use in development!)
WebSocketCheckOrigin func(r *http.Request) bool
// Pretty: Pretty-print JSON responses
Pretty bool
// GraphiQL: Enable GraphiQL interface (deprecated, use Playground instead)
GraphiQL bool
// Playground: Enable GraphQL Playground interface
Playground bool
// DEBUG mode skips validation and sanitization for easier development
// Default: false (validation enabled)
DEBUG bool
// RootObjectFn: Custom function to set up root object for each request
// Called before token extraction and user details fetching
RootObjectFn func(ctx context.Context, r *http.Request) map[string]interface{}
// TokenExtractorFn: Custom token extraction from request
// If not provided, default Bearer token extraction will be used
TokenExtractorFn func(*http.Request) string
// UserDetailsFn: Custom user details fetching based on token
// If not provided, user details will not be added to rootValue
// The details are accessible in resolvers via GetRootInfo(p, "details", &user)
//
// The function receives the request context and token, and returns:
// - ctx: Updated context with custom values (accessible via p.Context.Value() in resolvers)
// - details: User details (accessible via GetRootInfo(p, "details", &user) in resolvers)
// - error: Any error during user details fetching
//
// Example:
//
// UserDetailsFn: func(ctx context.Context, token string) (context.Context, interface{}, error) {
// user, err := validateJWT(token)
// if err != nil {
// return ctx, nil, err
// }
// // Add user ID to context for access in resolvers via p.Context.Value("userID")
// ctx = context.WithValue(ctx, "userID", user.ID)
// return ctx, user, nil
// }
UserDetailsFn func(ctx context.Context, token string) (context.Context, interface{}, error)
// EnableValidation: Enable query validation (depth, complexity, introspection checks)
// Default: false (validation disabled)
// When enabled: Max depth=10, Max aliases=4, Max complexity=200, Introspection blocked
// DEPRECATED: Use ValidationRules for more control
EnableValidation bool
// ValidationRules: Custom validation rules (takes precedence over EnableValidation)
// Set to nil or empty slice to disable validation
// Example:
// ValidationRules: []ValidationRule{
// NewMaxDepthRule(10),
// NewRequireAuthRule("mutation"),
// NewRoleRules(map[string][]string{
// "deleteUser": {"admin"},
// }),
// }
ValidationRules []ValidationRule
// ValidationOptions: Configure validation behavior (optional)
// Default: StopOnFirstError=false, SkipInDebug=true
ValidationOptions *ValidationOptions
// EnableSanitization: Enable response sanitization (removes field suggestions from errors)
// Default: false (sanitization disabled)
// Prevents information disclosure by removing "Did you mean X?" suggestions
EnableSanitization bool
}
GraphContext configures a GraphQL handler with schema, authentication, and security settings.
Schema Configuration (choose one):
- Schema: Use a pre-built graphql.Schema
- SchemaParams: Use the builder pattern with QueryFields and MutationFields
- Neither: A default "hello world" schema will be created
Security Modes:
- DEBUG mode (DEBUG: true): Disables all validation and sanitization for development
- Production mode (DEBUG: false): Enables validation and sanitization based on configuration flags
Authentication:
- TokenExtractorFn: Extract tokens from requests (defaults to Bearer token extraction)
- UserDetailsFn: Fetch user details from the extracted token
- RootObjectFn: Custom root object setup for advanced use cases
Example Development Setup:
ctx := &graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{
QueryFields: []graph.QueryField{getUserQuery()},
},
DEBUG: true,
Playground: true,
}
Example Production Setup:
ctx := &graph.GraphContext{
SchemaParams: &graph.SchemaBuilderParams{...},
DEBUG: false,
EnableValidation: true, // Max depth: 10, Max aliases: 4, Max complexity: 200
EnableSanitization: true, // Remove field suggestions from errors
Playground: false,
UserDetailsFn: func(ctx context.Context, token string) (context.Context, interface{}, error) {
user, err := validateJWT(token)
if err != nil {
return ctx, nil, err
}
ctx = context.WithValue(ctx, "userID", user.ID)
return ctx, user, nil
},
}
type HasIDInterface ¶ added in v1.1.1
type HasIDInterface interface {
GetID() string
}
HasIDInterface - implement this on your user struct for rate limiting
type HasPermissionsInterface ¶ added in v1.1.1
HasPermissionsInterface - implement this on your user struct for permission-based rules
type HasRolesInterface ¶ added in v1.1.1
HasRolesInterface - implement this on your user struct for role-based rules
type InMemoryPubSub ¶ added in v1.1.0
type InMemoryPubSub struct {
// contains filtered or unexported fields
}
InMemoryPubSub is a simple in-memory implementation of PubSub. It's suitable for development, testing, and single-instance deployments. For production multi-instance deployments, use RedisPubSub or similar.
func NewInMemoryPubSub ¶ added in v1.1.0
func NewInMemoryPubSub() *InMemoryPubSub
NewInMemoryPubSub creates a new in-memory PubSub implementation.
Example:
pubsub := graph.NewInMemoryPubSub()
defer pubsub.Close()
ctx := context.Background()
sub := pubsub.Subscribe(ctx, "events")
go func() {
for msg := range sub {
fmt.Println("Event:", string(msg.Data))
}
}()
pubsub.Publish(ctx, "events", map[string]string{"type": "user_created"})
func (*InMemoryPubSub) Close ¶ added in v1.1.0
func (p *InMemoryPubSub) Close() error
Close shuts down the PubSub and closes all active subscriptions.
func (*InMemoryPubSub) Publish ¶ added in v1.1.0
func (p *InMemoryPubSub) Publish(ctx context.Context, topic string, data interface{}) error
Publish sends data to all subscribers of the topic. Slow subscribers are skipped to prevent blocking.
func (*InMemoryPubSub) Subscribe ¶ added in v1.1.0
func (p *InMemoryPubSub) Subscribe(ctx context.Context, topic string) <-chan *Message
Subscribe creates a subscription to a topic. The subscription is automatically cleaned up when the context is canceled.
func (*InMemoryPubSub) Unsubscribe ¶ added in v1.1.0
func (p *InMemoryPubSub) Unsubscribe(ctx context.Context, subscriptionID string) error
Unsubscribe removes a subscription by ID (not commonly used with context-based cleanup).
type InputAuthorizer ¶ added in v1.2.3
InputAuthorizer runs before Validate. Failures surface as UNAUTHORIZED.
type InputNormalizer ¶ added in v1.2.3
type InputNormalizer interface{ Normalize() }
InputNormalizer lets an input struct normalize its fields (trim spaces, lowercase emails, etc.) after decoding and before validation.
type InputObjectInfo ¶ added in v1.2.4
type InputObjectInfo struct {
TypeName string `json:"typeName"`
Nullable bool `json:"nullable,omitempty"`
}
InputObjectInfo describes the input struct for mutations built with WithInputObject. TypeName is the Go type name; the dashboard can fetch field-level detail via standard graphql-go introspection.
type InputValidator ¶ added in v1.2.3
InputValidator runs after Normalize. A non-nil error short-circuits the resolver and is surfaced as INVALID_INPUT unless the returned error is already a *MutationError (in which case its Code is preserved).
type Introspectable ¶ added in v1.2.4
type Introspectable interface {
FieldInfo() FieldInfo
}
Introspectable is implemented by every QueryField, MutationField, and SubscriptionField this library produces. External tooling (dashboards, documentation generators, schema linters) can iterate a schema's fields and read their metadata without parsing Go source.
Because FieldInfo is a plain struct, the output is safe to JSON-serialize, pass across goroutines, and diff between builds.
Typical consumer:
for _, qf := range schemaParams.QueryFields {
info, ok := graph.Inspect(qf)
if !ok { continue }
fmt.Printf("%s(%v) -> %s [mws=%d]\n",
info.Name, info.Args, info.ReturnType, len(info.Middlewares))
}
type JSONTime ¶
JSONTime is a custom time type that handles flexible JSON time formats. It supports both RFC3339 strings and array formats commonly used in some APIs.
Supported input formats:
- RFC3339 string: "2024-01-15T14:30:00Z"
- Array format: [year, month, day, hour, minute, second, nanosecond]
- Minimum array: [2024, 1, 15] (time components default to 0)
Output format:
- Always RFC3339 string: "2024-01-15T14:30:00Z"
Example usage:
type Event struct {
Name string `json:"name"`
StartTime graph.JSONTime `json:"startTime"`
}
// Accepts: {"startTime": "2024-01-15T14:30:00Z"}
// Accepts: {"startTime": [2024, 1, 15, 14, 30, 0]}
// Outputs: {"startTime": "2024-01-15T14:30:00Z"}
func (JSONTime) MarshalJSON ¶
MarshalJSON implements json.Marshaler interface. Serializes JSONTime to RFC3339 format string.
func (JSONTime) Time ¶
Time converts JSONTime back to the standard time.Time type. This is useful when you need to perform time operations or comparisons.
Example:
var event Event
json.Unmarshal(data, &event)
standardTime := event.StartTime.Time()
// Now use standard time operations
if standardTime.After(time.Now()) { ... }
func (*JSONTime) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler interface. Deserializes from either RFC3339 string or array format.
Array format: [year, month, day, hour?, minute?, second?, nanosecond?] Missing time components default to 0. All times are assumed to be UTC.
type MaxAliasesRule ¶ added in v1.1.1
type MaxAliasesRule struct {
BaseRule
// contains filtered or unexported fields
}
MaxAliasesRule validates number of aliases
func (*MaxAliasesRule) Validate ¶ added in v1.1.1
func (r *MaxAliasesRule) Validate(ctx *ValidationContext) error
type MaxComplexityRule ¶ added in v1.1.1
type MaxComplexityRule struct {
BaseRule
// contains filtered or unexported fields
}
MaxComplexityRule validates query complexity
func (*MaxComplexityRule) Validate ¶ added in v1.1.1
func (r *MaxComplexityRule) Validate(ctx *ValidationContext) error
type MaxDepthRule ¶ added in v1.1.1
type MaxDepthRule struct {
BaseRule
// contains filtered or unexported fields
}
MaxDepthRule validates maximum query depth
func (*MaxDepthRule) Validate ¶ added in v1.1.1
func (r *MaxDepthRule) Validate(ctx *ValidationContext) error
type MaxTokensRule ¶ added in v1.1.1
type MaxTokensRule struct {
BaseRule
// contains filtered or unexported fields
}
MaxTokensRule limits query size by token count
func (*MaxTokensRule) Validate ¶ added in v1.1.1
func (r *MaxTokensRule) Validate(ctx *ValidationContext) error
type Message ¶ added in v1.1.0
type Message struct {
// Topic is the channel/topic name where this message was published
Topic string
// Data is the JSON-encoded payload
Data []byte
}
Message represents a published message with its topic and data payload.
type MiddlewareInfo ¶ added in v1.2.4
type MiddlewareInfo struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
MiddlewareInfo is the metadata recorded by WithNamedMiddleware. Middlewares added via plain WithMiddleware have Name == "anonymous".
type MultiValidationError ¶ added in v1.1.1
type MultiValidationError struct {
Errors []error
}
MultiValidationError combines multiple validation errors
func NewMultiValidationError ¶ added in v1.1.1
func NewMultiValidationError(errors []error) *MultiValidationError
func (*MultiValidationError) Error ¶ added in v1.1.1
func (e *MultiValidationError) Error() string
type MutationBuilder ¶ added in v1.2.3
MutationBuilder carries config common to every mutation kind. It deliberately exposes no WithResolver or Build — you must call Create/Update/Delete/Action/ Upsert to transition to a kind-specific builder.
func NewMutation ¶ added in v1.2.3
func NewMutation[T any, In any](name string) *MutationBuilder[T, In]
NewMutation is the single entry point for building mutations. The returned MutationBuilder cannot produce a GraphQL field on its own — you must pick a kind (Create, Update, Delete, Action, or Upsert) before calling WithResolver and Build. The kind determines the resolver signature.
Example:
graph.NewMutation[User, CreateUserInput]("createUser").
Create().
WithResolver(func(ctx context.Context, in CreateUserInput) (*User, error) {
return userService.Create(ctx, in)
}).
Build()
func (*MutationBuilder[T, In]) Action ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) Action() *ActionBuilder[T, In]
func (*MutationBuilder[T, In]) Create ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) Create() *CreateBuilder[T, In]
func (*MutationBuilder[T, In]) Delete ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) Delete() *DeleteBuilder[T, In]
func (*MutationBuilder[T, In]) Update ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) Update() *UpdateBuilder[T, In]
func (*MutationBuilder[T, In]) Upsert ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) Upsert() *UpsertBuilder[T, In]
func (*MutationBuilder[T, In]) Use ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) Use(mw ...FieldMiddleware) *MutationBuilder[T, In]
func (*MutationBuilder[T, In]) WithDescription ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) WithDescription(d string) *MutationBuilder[T, In]
func (*MutationBuilder[T, In]) WithInputName ¶ added in v1.2.3
func (b *MutationBuilder[T, In]) WithInputName(n string) *MutationBuilder[T, In]
type MutationError ¶ added in v1.2.3
func (*MutationError) Error ¶ added in v1.2.3
func (e *MutationError) Error() string
func (*MutationError) Extensions ¶ added in v1.2.3
func (e *MutationError) Extensions() map[string]interface{}
func (*MutationError) Unwrap ¶ added in v1.2.3
func (e *MutationError) Unwrap() error
type MutationField ¶
type MutationField interface {
// Serve returns the GraphQL field configuration
Serve() *graphql.Field
// Name returns the field name used in the GraphQL schema
Name() string
}
MutationField represents a GraphQL mutation field with its configuration. Implementations must provide both the field configuration and its name.
Use NewResolver to create MutationField instances:
mutation := graph.NewResolver[User]("createUser").
WithInputObject(CreateUserInput{}).
WithResolver(...).
BuildMutation()
type NoIntrospectionRule ¶ added in v1.1.1
type NoIntrospectionRule struct {
BaseRule
}
NoIntrospectionRule blocks introspection queries
func (*NoIntrospectionRule) Validate ¶ added in v1.1.1
func (r *NoIntrospectionRule) Validate(ctx *ValidationContext) error
type PageInfo ¶
type PageInfo struct {
HasNextPage bool `json:"hasNextPage" description:"Whether there are more pages"`
HasPreviousPage bool `json:"hasPreviousPage" description:"Whether there are previous pages"`
StartCursor string `json:"startCursor" description:"Cursor for the first item"`
EndCursor string `json:"endCursor" description:"Cursor for the last item"`
}
PageInfo contains pagination information
type PaginatedResponse ¶
type PaginatedResponse[T any] struct { Items []T `json:"items" description:"List of items"` TotalCount int `json:"totalCount" description:"Total number of items"` PageInfo PageInfo `json:"pageInfo" description:"Pagination information"` }
PaginatedResponse represents a paginated response structure
type PaginationArgs ¶
type PaginationArgs struct {
First *int `json:"first" description:"Number of items to fetch"`
After *string `json:"after" description:"Cursor to start after"`
Last *int `json:"last" description:"Number of items to fetch from end"`
Before *string `json:"before" description:"Cursor to start before"`
}
PaginationArgs contains pagination arguments
type Patch ¶ added in v1.2.3
type Patch[T any] struct { // contains filtered or unexported fields }
Patch wraps a decoded input with the set of fields the client sent. Only Update and Upsert resolvers receive a Patch — the framework constructs it from the raw args, so "omitted" and "set to zero value" are distinguishable.
func (Patch[T]) Apply ¶ added in v1.2.3
func (p Patch[T]) Apply(dst *T)
Apply copies only the fields that were present in the request onto dst.
func (Patch[T]) Presence ¶ added in v1.2.3
func (p Patch[T]) Presence() PresenceSet
type PatchInputValidator ¶ added in v1.2.3
type PatchInputValidator interface {
ValidatePatch(ctx context.Context, present PresenceSet) error
}
PatchInputValidator is preferred over InputValidator for Update and Upsert kinds, because it receives the set of fields the client actually sent.
type PermissionRule ¶ added in v1.1.1
type PermissionRule struct {
BaseRule
// contains filtered or unexported fields
}
PermissionRule validates a single field requires specific permissions
func (*PermissionRule) Validate ¶ added in v1.1.1
func (r *PermissionRule) Validate(ctx *ValidationContext) error
type PermissionRules ¶ added in v1.1.1
type PermissionRules struct {
BaseRule
// contains filtered or unexported fields
}
PermissionRules validates multiple fields with permission requirements
func (*PermissionRules) Validate ¶ added in v1.1.1
func (r *PermissionRules) Validate(ctx *ValidationContext) error
type PresenceSet ¶ added in v1.2.3
PresenceSet reports which fields were included in the client request. It is populated from the raw args map, not from the decoded struct, so "field sent with zero value" and "field omitted" are distinguishable.
type PubSub ¶ added in v1.1.0
type PubSub interface {
// Publish sends data to all subscribers of a topic.
// The data will be JSON-marshaled automatically.
//
// Returns an error if:
// - JSON marshaling fails
// - Context is canceled
// - PubSub is closed
Publish(ctx context.Context, topic string, data interface{}) error
// Subscribe creates a new subscription to a topic.
// Returns a channel that receives messages published to the topic.
//
// The subscription remains active until:
// - The context is canceled
// - Unsubscribe is called with the subscription ID
// - The PubSub is closed
//
// The returned channel will be closed when the subscription ends.
Subscribe(ctx context.Context, topic string) <-chan *Message
// Unsubscribe removes a subscription by its ID.
// The subscription's message channel will be closed.
Unsubscribe(ctx context.Context, subscriptionID string) error
// Close shuts down the PubSub system and closes all active subscriptions.
Close() error
}
PubSub defines the interface for publishing and subscribing to events. Implementations can use in-memory channels, Redis, Kafka, or other message brokers.
Example Usage:
pubsub := graph.NewInMemoryPubSub()
defer pubsub.Close()
// Subscribe to a topic
ctx := context.Background()
subscription := pubsub.Subscribe(ctx, "messages:channel1")
// Publish an event
pubsub.Publish(ctx, "messages:channel1", map[string]string{"text": "Hello"})
// Receive events
for msg := range subscription {
fmt.Println("Received:", string(msg.Data))
}
type QueryASTCache ¶ added in v1.2.3
type QueryASTCache struct {
// contains filtered or unexported fields
}
QueryASTCache caches parsed GraphQL ASTs keyed by the raw query string. It is safe for concurrent use. When the cache reaches its configured max size, the entire map is replaced — a coarse eviction that keeps the implementation lock-simple and predictable for steady-state traffic.
Rules walk the AST read-only; callers MUST NOT mutate cached ASTs.
func NewQueryASTCache ¶ added in v1.2.3
func NewQueryASTCache(maxEntries int) *QueryASTCache
NewQueryASTCache returns a cache bounded to maxEntries. A non-positive maxEntries is clamped to 1 to preserve cache semantics without growing unbounded.
type QueryField ¶
type QueryField interface {
// Serve returns the GraphQL field configuration
Serve() *graphql.Field
// Name returns the field name used in the GraphQL schema
Name() string
}
QueryField represents a GraphQL query field with its configuration. Implementations must provide both the field configuration and its name.
Use NewResolver to create QueryField instances:
query := graph.NewResolver[User]("user").
WithArgs(...).
WithResolver(...).
BuildQuery()
type RateLimitOption ¶ added in v1.1.1
type RateLimitOption func(*RateLimitRule)
RateLimitOption configures rate limiting behavior
func WithBudgetFunc ¶ added in v1.1.1
func WithBudgetFunc(fn func(userID string) (int, error)) RateLimitOption
WithBudgetFunc sets the function to get user's remaining budget
func WithBypassRoles ¶ added in v1.1.1
func WithBypassRoles(roles ...string) RateLimitOption
WithBypassRoles sets roles that bypass rate limiting (e.g., "admin", "service")
func WithCostPerUnit ¶ added in v1.1.1
func WithCostPerUnit(cost int) RateLimitOption
WithCostPerUnit sets the cost multiplier per complexity unit (default: 1)
type RateLimitRule ¶ added in v1.1.1
type RateLimitRule struct {
BaseRule
// contains filtered or unexported fields
}
RateLimitRule implements per-user rate limiting based on query complexity
func (*RateLimitRule) Validate ¶ added in v1.1.1
func (r *RateLimitRule) Validate(ctx *ValidationContext) error
type RequireAuthRule ¶ added in v1.1.1
type RequireAuthRule struct {
BaseRule
// contains filtered or unexported fields
}
RequireAuthRule requires authentication for specific operations or fields Simply checks if ctx.UserDetails != nil
func (*RequireAuthRule) Validate ¶ added in v1.1.1
func (r *RequireAuthRule) Validate(ctx *ValidationContext) error
type ResolveParams ¶
type ResolveParams graphql.ResolveParams
type Result ¶ added in v1.2.3
Result is the return value for an Upsert resolver. Created distinguishes insert from update so the generated payload type can expose it to clients.
type RoleRule ¶ added in v1.1.1
type RoleRule struct {
BaseRule
// contains filtered or unexported fields
}
RoleRule validates a single field requires specific roles
func (*RoleRule) Validate ¶ added in v1.1.1
func (r *RoleRule) Validate(ctx *ValidationContext) error
type RoleRules ¶ added in v1.1.1
type RoleRules struct {
BaseRule
// contains filtered or unexported fields
}
RoleRules validates multiple fields with role requirements
func (*RoleRules) Validate ¶ added in v1.1.1
func (r *RoleRules) Validate(ctx *ValidationContext) error
type RootInfo ¶ added in v1.2.2
type RootInfo map[string]interface{}
RootInfo wraps map[string]interface{} to implement RootInfoGetter. This allows using p.Info.RootValue with type-safe generic functions.
Usage:
// Extract user details from root value user := graph.GetRoot[UserDetails](graph.NewRootInfo(p), "details") // With error handling user, err := graph.GetRootE[UserDetails](graph.NewRootInfo(p), "details") // With default value token := graph.GetRootOr[string](graph.NewRootInfo(p), "token", "")
func NewRootInfo ¶ added in v1.2.2
func NewRootInfo(p ResolveParams) RootInfo
NewRootInfo extracts RootInfo from ResolveParams. Returns nil if RootValue is nil or not a map[string]interface{}.
Usage:
rootInfo := graph.NewRootInfo(p) user := graph.GetRoot[UserDetails](rootInfo, "details")
func (RootInfo) GetRootValue ¶ added in v1.2.2
GetRootValue implements RootInfoGetter interface
type RootInfoGetter ¶ added in v1.2.2
RootInfoGetter is an interface for types that can provide root value data by name. This allows GetRoot[T], GetRootE[T], GetRootOr[T], and MustGetRoot[T] to work with root value data extracted from ResolveParams.
type SchemaBuilder ¶
type SchemaBuilder struct {
// contains filtered or unexported fields
}
SchemaBuilder builds GraphQL schemas from QueryFields and MutationFields. Use NewSchemaBuilder to create an instance and Build() to generate the schema.
func NewSchemaBuilder ¶
func NewSchemaBuilder(params SchemaBuilderParams) *SchemaBuilder
NewSchemaBuilder creates a new schema builder with the provided query and mutation fields.
Example:
params := graph.SchemaBuilderParams{
QueryFields: []graph.QueryField{getUserQuery()},
MutationFields: []graph.MutationField{createUserMutation()},
}
builder := graph.NewSchemaBuilder(params)
schema, err := builder.Build()
func (*SchemaBuilder) Build ¶
func (sb *SchemaBuilder) Build() (graphql.Schema, error)
Build constructs and returns a graphql.Schema from the configured fields. It creates Query and Mutation root types based on the provided fields.
Returns an error if:
- Schema construction fails due to type conflicts
- Field configurations are invalid
The schema can have:
- Only queries (no mutations)
- Only mutations (no queries)
- Both queries and mutations
- Neither (empty schema)
type SchemaBuilderParams ¶
type SchemaBuilderParams struct {
// QueryFields: List of query fields to include in the schema
QueryFields []QueryField `group:"query_fields"`
// MutationFields: List of mutation fields to include in the schema
MutationFields []MutationField `group:"mutation_fields"`
// SubscriptionFields: List of subscription fields to include in the schema
// Requires WebSocket support and PubSub configuration
SubscriptionFields []SubscriptionField `group:"subscription_fields"`
}
SchemaBuilderParams configures the fields for building a GraphQL schema. Use this with NewSchemaBuilder to construct schemas using the builder pattern.
Example:
params := graph.SchemaBuilderParams{
QueryFields: []graph.QueryField{
getUserQuery(),
listUsersQuery(),
},
MutationFields: []graph.MutationField{
createUserMutation(),
updateUserMutation(),
},
}
schema, err := graph.NewSchemaBuilder(params).Build()
type SubscriptionField ¶ added in v1.1.0
type SubscriptionField interface {
// Serve returns the GraphQL field configuration
Serve() *graphql.Field
// Name returns the subscription field name
Name() string
}
SubscriptionField represents a GraphQL subscription field that can be added to a schema. It follows the same interface pattern as QueryField and MutationField.
Create subscription fields using NewSubscription:
sub := NewSubscription[MessageEvent]("messageAdded").
WithArgs(...).
WithResolver(...).
BuildSubscription()
type SubscriptionFilterFn ¶ added in v1.1.0
type SubscriptionFilterFn[T any] func(ctx context.Context, data *T, p ResolveParams) bool
SubscriptionFilterFn filters events before sending them to clients. Return true to send the event, false to skip it.
This is useful for:
- User-specific filtering based on permissions
- Content-based filtering based on subscription arguments
- Rate limiting or throttling
Example:
func(ctx context.Context, data *MessageEvent, p ResolveParams) bool {
userID, _ := GetRootString(p, "userID")
return canUserViewMessage(userID, data.ID)
}
type SubscriptionResolveFn ¶ added in v1.1.0
type SubscriptionResolveFn[T any] func(ctx context.Context, p ResolveParams) (<-chan *T, error)
SubscriptionResolveFn is the resolver function for subscriptions. It returns a channel that emits events of type T.
The resolver should:
- Create a buffered channel to prevent blocking
- Start a goroutine to handle event publishing
- Close the channel when done or context is canceled
- Handle errors by returning nil channel and an error
Example:
func(ctx context.Context, p ResolveParams) (<-chan *MessageEvent, error) {
channelID, _ := GetArgString(p, "channelID")
events := make(chan *MessageEvent, 10)
subscription := pubsub.Subscribe(ctx, "messages:"+channelID)
go func() {
defer close(events)
for msg := range subscription {
var event MessageEvent
if err := json.Unmarshal(msg.Data, &event); err == nil {
events <- &event
}
}
}()
return events, nil
}
type SubscriptionResolver ¶ added in v1.1.0
type SubscriptionResolver[T any] struct { // contains filtered or unexported fields }
SubscriptionResolver builds type-safe subscription fields with extensive customization capabilities. It provides a fluent API similar to UnifiedResolver for building subscriptions.
Type Parameters:
- T: The Go struct type that will be sent to subscribers
The resolver function should return a channel that emits events of type T. When the channel is closed or the context is canceled, the subscription ends.
Basic Usage:
type MessageEvent struct {
ID string `json:"id"`
Content string `json:"content"`
Timestamp time.Time `json:"timestamp"`
}
sub := NewSubscription[MessageEvent]("messageAdded").
WithDescription("Subscribe to new messages").
WithArgs(graphql.FieldConfigArgument{
"channelID": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
}).
WithResolver(func(ctx context.Context, p ResolveParams) (<-chan *MessageEvent, error) {
channelID, _ := GetArgString(p, "channelID")
events := make(chan *MessageEvent)
subscription := pubsub.Subscribe(ctx, "messages:"+channelID)
go func() {
defer close(events)
for msg := range subscription {
var event MessageEvent
json.Unmarshal(msg.Data, &event)
events <- &event
}
}()
return events, nil
}).
WithFilter(func(ctx context.Context, data *MessageEvent, p ResolveParams) bool {
// Optional: filter events before sending to client
return true
}).
WithMiddleware(AuthMiddleware("user")).
BuildSubscription()
Advanced Features:
// Middleware support
sub := NewSubscription[Event]("events").
WithMiddleware(LoggingMiddleware).
WithMiddleware(AuthMiddleware("admin")).
// ... rest of configuration
// Field-level customization (like UnifiedResolver)
sub := NewSubscription[ComplexEvent]("complexEvent").
WithFieldResolver("computedField", func(p ResolveParams) (interface{}, error) {
event := p.Source.(ComplexEvent)
return computeValue(event), nil
}).
// ... rest of configuration
func NewSubscription ¶ added in v1.1.0
func NewSubscription[T any](name string) *SubscriptionResolver[T]
NewSubscription creates a new subscription resolver with the specified name. The type parameter T determines the event type that will be sent to subscribers.
Example:
type UserStatusEvent struct {
UserID string `json:"userID"`
Status string `json:"status"`
Timestamp time.Time `json:"timestamp"`
}
sub := NewSubscription[UserStatusEvent]("userStatusChanged").
WithArgs(graphql.FieldConfigArgument{
"userID": &graphql.ArgumentConfig{Type: graphql.String},
}).
WithResolver(func(ctx context.Context, p ResolveParams) (<-chan *UserStatusEvent, error) {
// Implementation
}).
BuildSubscription()
func (*SubscriptionResolver[T]) BuildSubscription ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) BuildSubscription() SubscriptionField
BuildSubscription builds and returns a SubscriptionField that can be added to the schema.
This method:
- Auto-generates the GraphQL type from the Go struct T
- Applies field-level customizations and middleware
- Creates the subscription and resolve functions
- Registers the type in the global type registry
Example:
sub := NewSubscription[MessageEvent]("messageAdded").
WithArgs(...).
WithResolver(...).
BuildSubscription()
// Add to schema
schema := graph.NewSchemaBuilder(graph.SchemaBuilderParams{
SubscriptionFields: []graph.SubscriptionField{sub},
})
func (*SubscriptionResolver[T]) WithArgs ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) WithArgs(args graphql.FieldConfigArgument) *SubscriptionResolver[T]
WithArgs sets custom arguments for the subscription.
Example:
WithArgs(graphql.FieldConfigArgument{
"channelID": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
Description: "Channel to subscribe to",
},
"filter": &graphql.ArgumentConfig{
Type: graphql.String,
Description: "Optional filter pattern",
},
})
func (*SubscriptionResolver[T]) WithDescription ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) WithDescription(desc string) *SubscriptionResolver[T]
WithDescription adds a description to the subscription field.
func (*SubscriptionResolver[T]) WithFieldMiddleware ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) WithFieldMiddleware(fieldName string, middleware FieldMiddleware) *SubscriptionResolver[T]
WithFieldMiddleware adds middleware to a specific field in the event type.
Example:
WithFieldMiddleware("sensitiveData", AuthMiddleware("admin"))
func (*SubscriptionResolver[T]) WithFieldResolver ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) WithFieldResolver(fieldName string, resolver graphql.FieldResolveFn) *SubscriptionResolver[T]
WithFieldResolver overrides the resolver for a specific field in the event type. This allows customizing how specific fields are resolved.
Example:
WithFieldResolver("author", func(p ResolveParams) (interface{}, error) {
event := p.Source.(MessageEvent)
return userService.GetByID(event.AuthorID), nil
})
func (*SubscriptionResolver[T]) WithFilter ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) WithFilter(fn SubscriptionFilterFn[T]) *SubscriptionResolver[T]
WithFilter adds a filter function to filter events before sending to clients. Only events that pass the filter (return true) will be sent.
Example:
WithFilter(func(ctx context.Context, data *MessageEvent, p ResolveParams) bool {
userID, _ := GetRootString(p, "userID")
return data.AuthorID != userID // Don't send user's own messages
})
func (*SubscriptionResolver[T]) WithMiddleware ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) WithMiddleware(middleware FieldMiddleware) *SubscriptionResolver[T]
WithMiddleware adds middleware to the subscription resolver. Middleware is executed in the order it's added.
Example:
WithMiddleware(LoggingMiddleware).
WithMiddleware(AuthMiddleware("user"))
func (*SubscriptionResolver[T]) WithResolver ¶ added in v1.1.0
func (s *SubscriptionResolver[T]) WithResolver(fn SubscriptionResolveFn[T]) *SubscriptionResolver[T]
WithResolver sets the subscription resolver function. The resolver should return a channel that emits events of type T.
Example:
WithResolver(func(ctx context.Context, p ResolveParams) (<-chan *MessageEvent, error) {
channelID, _ := GetArgString(p, "channelID")
events := make(chan *MessageEvent, 10)
subscription := pubsub.Subscribe(ctx, "messages:"+channelID)
go func() {
defer close(events)
for msg := range subscription {
var event MessageEvent
if err := json.Unmarshal(msg.Data, &event); err == nil {
events <- &event
}
}
}()
return events, nil
})
type UnifiedResolver ¶
type UnifiedResolver[T any] struct { // contains filtered or unexported fields }
UnifiedResolver handles all GraphQL resolver scenarios with field-level customization
func NewResolver ¶
func NewResolver[T any](name string) *UnifiedResolver[T]
func (*UnifiedResolver[T]) AsList ¶
func (r *UnifiedResolver[T]) AsList() *UnifiedResolver[T]
Query Configuration
func (*UnifiedResolver[T]) AsMutation ¶
func (r *UnifiedResolver[T]) AsMutation() *UnifiedResolver[T]
Mutation Configuration
func (*UnifiedResolver[T]) AsPaginated ¶
func (r *UnifiedResolver[T]) AsPaginated() *UnifiedResolver[T]
func (*UnifiedResolver[T]) Build ¶
func (r *UnifiedResolver[T]) Build() interface{}
func (*UnifiedResolver[T]) BuildMutation ¶
func (r *UnifiedResolver[T]) BuildMutation() MutationField
func (*UnifiedResolver[T]) BuildQuery ¶
func (r *UnifiedResolver[T]) BuildQuery() QueryField
Build Methods
func (*UnifiedResolver[T]) FieldInfo ¶ added in v1.2.4
func (r *UnifiedResolver[T]) FieldInfo() FieldInfo
FieldInfo returns a serializable snapshot of this resolver's metadata. Calling this is cheap — it reads already-stored fields and runs Serve() once for the return type. Safe to call multiple times.
func (*UnifiedResolver[T]) GetArgsDefinition ¶ added in v1.2.4
func (r *UnifiedResolver[T]) GetArgsDefinition() graphql.FieldConfigArgument
GetArgsDefinition returns the underlying graphql.FieldConfigArgument. Useful for tools that want the raw graphql-go types (e.g. to fabricate their own introspection query programmatically).
func (*UnifiedResolver[T]) GetMiddlewareCount ¶ added in v1.2.4
func (r *UnifiedResolver[T]) GetMiddlewareCount() int
GetMiddlewareCount returns the number of middlewares on the main resolver. For richer information use FieldInfo().Middlewares.
func (*UnifiedResolver[T]) GetMiddlewareInfos ¶ added in v1.2.4
func (r *UnifiedResolver[T]) GetMiddlewareInfos() []MiddlewareInfo
GetMiddlewareInfos returns the MiddlewareInfo slice in application order. Entries for middlewares added via plain WithMiddleware have Name == "anonymous".
func (*UnifiedResolver[T]) IsDeprecated ¶ added in v1.2.4
func (r *UnifiedResolver[T]) IsDeprecated() bool
IsDeprecated reports whether WithDeprecated was applied.
func (*UnifiedResolver[T]) IsList ¶ added in v1.2.4
func (r *UnifiedResolver[T]) IsList() bool
IsList reports whether AsList was used (or the return type is a slice).
func (*UnifiedResolver[T]) IsMutation ¶ added in v1.2.4
func (r *UnifiedResolver[T]) IsMutation() bool
IsMutation reports whether BuildMutation or AsMutation was used.
func (*UnifiedResolver[T]) IsPaginated ¶ added in v1.2.4
func (r *UnifiedResolver[T]) IsPaginated() bool
IsPaginated reports whether AsPaginated was used.
func (*UnifiedResolver[T]) Name ¶
func (r *UnifiedResolver[T]) Name() string
Interface Implementation
func (*UnifiedResolver[T]) Serve ¶
func (r *UnifiedResolver[T]) Serve() *graphql.Field
func (*UnifiedResolver[T]) WithArg ¶ added in v1.1.7
func (r *UnifiedResolver[T]) WithArg(name string, argType interface{}) *UnifiedResolver[T]
WithArg adds an argument to the resolver. Supports: - Go primitive types: string, int, int64, float64, bool (pass zero value or instance) - GraphQL types: graph.String, graph.Int, graph.Float, graph.Boolean, graph.ID - Struct types: any struct will be converted to GraphQL InputObject (supports deeply nested structs) - Slices: []Type will be converted to [Type] GraphQL list
Usage:
// With Go primitive types (recommended)
NewResolver[User]("user").
WithArg("id", ""). // string
WithArg("limit", 0). // int
WithArg("active", false). // bool
WithResolver(func(p ResolveParams) (*User, error) {
id := Get[string](ArgsMap(p.Args), "id")
return userService.GetByID(id)
})
// With struct type (deeply nested supported)
type AddressInput struct {
Street string `json:"street"`
City string `json:"city"`
}
type UserInput struct {
Name string `json:"name"`
Address AddressInput `json:"address"`
}
NewResolver[User]("createUser").
WithArg("input", UserInput{}).
WithResolver(func(p ResolveParams) (*User, error) {
input := Get[UserInput](ArgsMap(p.Args), "input")
return userService.Create(input)
})
func (*UnifiedResolver[T]) WithArgDefault ¶ added in v1.1.7
func (r *UnifiedResolver[T]) WithArgDefault(name string, argType interface{}, defaultValue interface{}) *UnifiedResolver[T]
WithArgDefault adds an argument with a default value
func (*UnifiedResolver[T]) WithArgRequired ¶ added in v1.1.7
func (r *UnifiedResolver[T]) WithArgRequired(name string, argType interface{}) *UnifiedResolver[T]
WithArgRequired adds a required argument to the resolver
func (*UnifiedResolver[T]) WithArgValidator ¶ added in v1.2.4
func (r *UnifiedResolver[T]) WithArgValidator(argName string, validators ...Validator) *UnifiedResolver[T]
WithArgValidator attaches one or more validators to a named argument. Validators run after graphql-go parses input but before the resolver fires; the first failing validator aborts with its error. Metadata surfaces on FieldInfo.Args[i].Validators for dashboards.
Use the built-in constructors (Required, StringLength, StringMatch, OneOf, IntRange, Custom) rather than populating Validator by hand.
r.WithArgValidator("title", graph.Required(), graph.StringLength(1, 100))
r.WithArgValidator("email", graph.StringMatch(`^\S+@\S+$`, "must be an email"))
func (*UnifiedResolver[T]) WithArgs ¶
func (r *UnifiedResolver[T]) WithArgs(args graphql.FieldConfigArgument) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithArgsFromStruct ¶
func (r *UnifiedResolver[T]) WithArgsFromStruct(structType interface{}) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithAsyncField ¶
func (r *UnifiedResolver[T]) WithAsyncField(fieldName string, resolver graphql.FieldResolveFn) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithCachedField ¶
func (r *UnifiedResolver[T]) WithCachedField(fieldName string, cacheKeyFunc func(graphql.ResolveParams) string, resolver graphql.FieldResolveFn) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithComputedField ¶
func (r *UnifiedResolver[T]) WithComputedField(name string, fieldType graphql.Output, resolver graphql.FieldResolveFn) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithCustomField ¶
func (r *UnifiedResolver[T]) WithCustomField(name string, field *graphql.Field) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithDeprecated ¶ added in v1.2.4
func (r *UnifiedResolver[T]) WithDeprecated(reason string) *UnifiedResolver[T]
WithDeprecated marks this field deprecated. The reason propagates to the generated *graphql.Field and to FieldInfo.DeprecationReason so both GraphQL clients (via introspection) and tools see it.
func (*UnifiedResolver[T]) WithDescription ¶
func (r *UnifiedResolver[T]) WithDescription(desc string) *UnifiedResolver[T]
Basic Configuration
func (*UnifiedResolver[T]) WithFieldMiddleware ¶
func (r *UnifiedResolver[T]) WithFieldMiddleware(fieldName string, middleware FieldMiddleware) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithFieldResolver ¶
func (r *UnifiedResolver[T]) WithFieldResolver(fieldName string, resolver graphql.FieldResolveFn) *UnifiedResolver[T]
Field-Level Customization
func (*UnifiedResolver[T]) WithFieldResolvers ¶
func (r *UnifiedResolver[T]) WithFieldResolvers(overrides map[string]graphql.FieldResolveFn) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithInputObject ¶
func (r *UnifiedResolver[T]) WithInputObject(inputType interface{}) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithInputObjectFieldName ¶
func (r *UnifiedResolver[T]) WithInputObjectFieldName(name string) *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithInputObjectNullable ¶
func (r *UnifiedResolver[T]) WithInputObjectNullable() *UnifiedResolver[T]
func (*UnifiedResolver[T]) WithLazyField ¶
func (r *UnifiedResolver[T]) WithLazyField(fieldName string, loader func(interface{}) (interface{}, error)) *UnifiedResolver[T]
Utility Methods for Field Configuration
func (*UnifiedResolver[T]) WithMiddleware ¶
func (r *UnifiedResolver[T]) WithMiddleware(middleware FieldMiddleware) *UnifiedResolver[T]
WithMiddleware adds middleware to the main resolver. Middleware functions are applied in the order they are added (first added = outermost layer). This is the foundation for all resolver-level middleware (auth, logging, caching, etc.).
Example usage:
NewResolver[User]("user").
WithMiddleware(LoggingMiddleware).
WithMiddleware(AuthMiddleware("admin")).
WithResolver(func(p ResolveParams) (*User, error) {
return userService.GetByID(p.Args["id"].(int))
}).
BuildQuery()
func (*UnifiedResolver[T]) WithNamedMiddleware ¶ added in v1.2.4
func (r *UnifiedResolver[T]) WithNamedMiddleware(name, description string, middleware FieldMiddleware) *UnifiedResolver[T]
WithNamedMiddleware adds a middleware with a display name and description. Prefer this over WithMiddleware when the middleware's identity matters to tools that render the resolver — dashboards, documentation generators, or a lint step that enforces "every mutation must have an 'auth' middleware".
The name shows up verbatim in FieldInfo.Middlewares[i].Name, so keep it kebab-case and stable ("auth", "rate-limit", "permission:admin").
r.WithNamedMiddleware("auth", "Bearer token validation", AuthMiddleware)
r.WithNamedMiddleware("permission:admin", "Admin role required",
PermissionMiddleware([]string{"admin"}))
func (*UnifiedResolver[T]) WithPermission ¶
func (r *UnifiedResolver[T]) WithPermission(middleware FieldMiddleware) *UnifiedResolver[T]
WithPermission adds permission middleware to the resolver (similar to Python @permission_classes decorator) This is now just a convenience wrapper around WithMiddleware for backwards compatibility
func (*UnifiedResolver[T]) WithResolver ¶
func (r *UnifiedResolver[T]) WithResolver(resolver func(ResolveParams) (*T, error)) *UnifiedResolver[T]
WithResolver sets a type-safe resolver function that returns *T instead of interface{} This provides better type safety and eliminates the need for type assertions or casts.
WithResolver requires the function signature:
- func(p ResolveParams) (*T, error)
Access arguments using type-safe getter functions with ArgsMap:
- Get[T](ArgsMap(p.Args), "key") - returns zero value if not found
- GetE[T](ArgsMap(p.Args), "key") - returns error if not found
- GetOr[T](ArgsMap(p.Args), "key", defaultVal) - returns default if not found
- MustGet[T](ArgsMap(p.Args), "key") - panics if not found
Example usage:
NewResolver[User]("user").
WithArg("id", graph.String).
WithResolver(func(p graph.ResolveParams) (*User, error) {
id := graph.Get[string](graph.ArgsMap(p.Args), "id")
return userService.GetByID(id)
}).BuildQuery()
NewResolver[User]("users").
AsList().
WithResolver(func(p graph.ResolveParams) (*[]User, error) {
users := userService.List()
return &users, nil
}).BuildQuery()
NewResolver[string]("hello").
WithResolver(func(p graph.ResolveParams) (*string, error) {
msg := "Hello, World!"
return &msg, nil
}).BuildQuery()
func (*UnifiedResolver[T]) WithTypedResolver ¶
func (r *UnifiedResolver[T]) WithTypedResolver(typedResolver interface{}) *UnifiedResolver[T]
Typed Resolver Support - allows direct struct parameters instead of graphql.ResolveParams
Example usage:
func resolveUser(args GetUserArgs) (*User, error) {
return &User{ID: args.ID, Name: "User"}, nil
}
NewResolver[User]("user", "User").
WithTypedResolver(resolveUser).
BuildQuery()
type UpdateBuilder ¶ added in v1.2.3
func (*UpdateBuilder[T, In]) Build ¶ added in v1.2.3
func (b *UpdateBuilder[T, In]) Build() MutationField
func (*UpdateBuilder[T, In]) WithResolver ¶ added in v1.2.3
func (b *UpdateBuilder[T, In]) WithResolver( fn func(ctx context.Context, p Patch[In]) (*T, error), ) *UpdateBuilder[T, In]
type UpsertBuilder ¶ added in v1.2.3
func (*UpsertBuilder[T, In]) Build ¶ added in v1.2.3
func (b *UpsertBuilder[T, In]) Build() MutationField
func (*UpsertBuilder[T, In]) WithResolver ¶ added in v1.2.3
func (b *UpsertBuilder[T, In]) WithResolver( fn func(ctx context.Context, p Patch[In]) (Result[T], error), ) *UpsertBuilder[T, In]
type ValidationContext ¶ added in v1.1.1
type ValidationContext struct {
// GraphQL query components
Query string
Document *ast.Document
Schema *graphql.Schema
Variables map[string]interface{}
// Request context
Request *http.Request
// User details from UserDetailsFn (can be nil if not authenticated)
// Validation rules can type-assert this to whatever structure they need
UserDetails interface{}
}
ValidationContext provides all necessary information for validation
type ValidationError ¶ added in v1.1.1
ValidationError provides detailed error information
func (*ValidationError) Error ¶ added in v1.1.1
func (e *ValidationError) Error() string
type ValidationOptions ¶ added in v1.1.1
type ValidationOptions struct {
// StopOnFirstError stops validation after first error
StopOnFirstError bool
// SkipInDebug skips validation when DEBUG=true
SkipInDebug bool
// QueryCache, when non-nil, caches parsed ASTs keyed by query string so
// repeated requests with the same query skip the parser.
QueryCache *QueryASTCache
}
ValidationOptions configures validation behavior
type ValidationRule ¶ added in v1.1.1
type ValidationRule interface {
// Name returns a unique identifier for this rule
Name() string
// Validate executes the rule against the parsed query
// Returns nil if valid, error if validation fails
Validate(ctx *ValidationContext) error
// Enabled checks if this rule should be executed
Enabled() bool
// Enable enables the rule
Enable()
// Disable disables the rule
Disable()
}
ValidationRule represents a single validation rule that can be applied to GraphQL queries
func CombineRules ¶ added in v1.1.1
func CombineRules(ruleSets ...[]ValidationRule) []ValidationRule
CombineRules combines multiple rule sets into one
Example:
rules := CombineRules(
SecurityRules,
[]ValidationRule{NewRequireAuthRule("mutation")},
)
func DefaultValidationRules ¶ added in v1.1.1
func DefaultValidationRules() []ValidationRule
DefaultValidationRules returns the default validation rules Equivalent to EnableValidation=true
func DevelopmentValidationRules ¶ added in v1.1.1
func DevelopmentValidationRules() []ValidationRule
DevelopmentValidationRules returns lenient development rules
func NewBlockedFieldsRule ¶ added in v1.1.1
func NewBlockedFieldsRule(fields ...string) ValidationRule
NewBlockedFieldsRule creates a new blocked fields rule
Example:
NewBlockedFieldsRule("internalUsers", "deprecatedField")
func NewMaxAliasesRule ¶ added in v1.1.1
func NewMaxAliasesRule(maxAliases int) ValidationRule
NewMaxAliasesRule creates a new max aliases validation rule
func NewMaxComplexityRule ¶ added in v1.1.1
func NewMaxComplexityRule(maxComplexity int) ValidationRule
NewMaxComplexityRule creates a new max complexity validation rule
func NewMaxDepthRule ¶ added in v1.1.1
func NewMaxDepthRule(maxDepth int) ValidationRule
NewMaxDepthRule creates a new max depth validation rule
func NewMaxTokensRule ¶ added in v1.1.1
func NewMaxTokensRule(maxTokens int) ValidationRule
NewMaxTokensRule creates a new max tokens validation rule
func NewNoIntrospectionRule ¶ added in v1.1.1
func NewNoIntrospectionRule() ValidationRule
NewNoIntrospectionRule creates a new no introspection validation rule
func NewPermissionRule ¶ added in v1.1.1
func NewPermissionRule(field string, permissions ...string) ValidationRule
NewPermissionRule creates a new permission validation rule for a single field
Example:
NewPermissionRule("sensitiveData", "read:sensitive")
NewPermissionRule("exportData", "export:data", "admin:all")
func NewPermissionRules ¶ added in v1.1.1
func NewPermissionRules(config map[string][]string) ValidationRule
NewPermissionRules creates a batch permission validation rule config maps field names to required permissions
Example:
NewPermissionRules(map[string][]string{
"sensitiveData": {"read:sensitive"},
"exportData": {"export:data"},
"adminPanel": {"admin:access"},
})
func NewRateLimitRule ¶ added in v1.1.1
func NewRateLimitRule(opts ...RateLimitOption) ValidationRule
NewRateLimitRule creates a new rate limiting rule with optional configuration
Example:
NewRateLimitRule(
WithBudgetFunc(getBudgetFromRedis),
WithCostPerUnit(2),
WithBypassRoles("admin", "service"),
)
func NewRequireAuthRule ¶ added in v1.1.1
func NewRequireAuthRule(targets ...string) ValidationRule
NewRequireAuthRule creates a new require authentication rule Targets can be operation types ("mutation", "subscription", "query") or field names
Example:
NewRequireAuthRule("mutation", "subscription") // Require auth for all mutations and subscriptions
NewRequireAuthRule("deleteUser", "updateUser") // Require auth for specific fields
func NewRoleRule ¶ added in v1.1.1
func NewRoleRule(field string, roles ...string) ValidationRule
NewRoleRule creates a new role validation rule for a single field
Example:
NewRoleRule("deleteUser", "admin")
NewRoleRule("viewAuditLog", "admin", "auditor")
func NewRoleRules ¶ added in v1.1.1
func NewRoleRules(config map[string][]string) ValidationRule
NewRoleRules creates a batch role validation rule config maps field names to required roles
Example:
NewRoleRules(map[string][]string{
"deleteUser": {"admin"},
"viewAuditLog": {"admin", "auditor"},
"approveOrder": {"admin", "manager"},
})
func ProductionValidationRules ¶ added in v1.1.1
func ProductionValidationRules() []ValidationRule
ProductionValidationRules returns recommended production rules
type Validator ¶ added in v1.2.4
type Validator struct {
Info ValidatorInfo
Fn ArgValidator
}
Validator pairs a runtime function with its metadata. Use the helpers in validators.go (Required, StringLength, StringMatch, ...) to construct these rather than populating by hand.
func Custom ¶ added in v1.2.4
func Custom(name, message string, fn ArgValidator) Validator
Custom wraps an arbitrary validator function with a displayable name. The kind is always "custom"; use the message/details fields of ValidatorInfo for anything you want the dashboard to show.
r.WithArgValidator("slug", graph.Custom("kebab-case", "lowercase letters and hyphens only",
func(v any) error { ... }))
func IntRange ¶ added in v1.2.4
IntRange enforces min <= value <= max for Int args. Use math.MinInt / math.MaxInt to skip a bound.
func OneOf ¶ added in v1.2.4
OneOf restricts the value to the given allowed set. Works for strings and ints (most GraphQL scalars that survive JSON decoding).
func Required ¶ added in v1.2.4
func Required() Validator
Required rejects nil / missing values. Useful for "Int" args where the GraphQL type is nullable but the business rule isn't.
func StringLength ¶ added in v1.2.4
StringLength enforces min <= len(value) <= max for string args. Use -1 for min or max to skip that side of the check.
func StringMatch ¶ added in v1.2.4
StringMatch returns a validator that accepts the string arg iff it matches the regex. The optional message replaces the default "invalid format" text. Panics at build time if the regex is invalid — that's a programmer bug.
type ValidatorInfo ¶ added in v1.2.4
type ValidatorInfo struct {
Kind string `json:"kind"`
Message string `json:"message,omitempty"`
Details any `json:"details,omitempty"`
}
ValidatorInfo describes a per-argument validator for dashboard display. The Kind field identifies the validator family (so a UI can render a matching icon or tooltip); Details holds the parameters (e.g. {"min":1,"max":100} for StringLength).
type WSMessage ¶ added in v1.1.0
type WSMessage struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Payload map[string]interface{} `json:"payload,omitempty"`
}
WSMessage represents a GraphQL WebSocket Protocol message. This follows the graphql-ws protocol specification.
type WebSocketManager ¶ added in v1.1.0
type WebSocketManager struct {
// contains filtered or unexported fields
}
WebSocketManager manages WebSocket connections for GraphQL subscriptions. It handles connection lifecycle, authentication, and message routing.
func (*WebSocketManager) CloseAllConnections ¶ added in v1.1.0
func (m *WebSocketManager) CloseAllConnections()
CloseAllConnections closes all active WebSocket connections. This is useful for graceful shutdown.
func (*WebSocketManager) HandleWebSocket ¶ added in v1.1.0
func (m *WebSocketManager) HandleWebSocket(w http.ResponseWriter, r *http.Request)
HandleWebSocket upgrades HTTP connections to WebSocket and manages the connection lifecycle.
type WebSocketParams ¶ added in v1.1.0
type WebSocketParams struct {
// Schema: The GraphQL schema with subscription fields
Schema *graphql.Schema
// PubSub: The PubSub system for event distribution
PubSub PubSub
// CheckOrigin: Function to check WebSocket upgrade origin
// If nil, all origins are allowed (development only!)
CheckOrigin func(r *http.Request) bool
// AuthFn: Authentication function to extract user details from request
// Called during connection_init phase
AuthFn func(r *http.Request) (interface{}, error)
// RootObjectFn: Custom function to set up root object for each connection
// Similar to HTTP handler's RootObjectFn
RootObjectFn func(ctx context.Context, r *http.Request) map[string]interface{}
// PingInterval: Interval for sending ping messages (default: 30 seconds)
// Set to 0 to disable automatic pinging
PingInterval time.Duration
// ConnectionTimeout: Timeout for connection_init message (default: 10 seconds)
ConnectionTimeout time.Duration
}
WebSocketParams configures the WebSocket handler for subscriptions.
Source Files
¶
- doc.go
- graphql_generator.go
- graphql_schema_builder.go
- graphql_subscription_resolver.go
- graphql_unified_resolver.go
- graphql_validation.go
- graphql_validation_auth.go
- graphql_validation_example.go
- graphql_validation_presets.go
- graphql_validation_ratelimit.go
- graphql_validation_rules.go
- graphql_validation_security.go
- handler.go
- introspection.go
- json.go
- mutation.go
- pubsub.go
- time.go
- types.go
- utils.go
- validators.go
- websocket.go