Skip to main content

Overview

The graphql middleware provides GraphQL query validation and security features including depth limiting, complexity analysis, introspection control, and field blocking. Use it when you need:
  • Query depth and complexity validation
  • Introspection query control
  • Field-level access control
  • Operation allowlisting
  • Protection against malicious queries

Installation

import "github.com/go-mizu/mizu/middlewares/graphql"

Quick Start

app := mizu.New()

// Use default validation (max depth: 10, max complexity: 100)
app.Use(graphql.New())

app.Post("/graphql", yourGraphQLHandler)

Configuration

Options

OptionTypeDefaultDescription
MaxDepthint10Maximum query depth allowed
MaxComplexityint100Maximum query complexity allowed
DisableIntrospectionboolfalseDisable introspection queries
AllowedOperations[]stringnilWhitelist of allowed operation names
BlockedFields[]stringnilList of fields to block
ErrorHandlerfunc(*mizu.Ctx, error) errornilCustom error handler

Examples

Basic Setup

// Use with default settings
app.Use(graphql.New())

app.Post("/graphql", yourGraphQLHandler)

Custom Depth and Complexity Limits

app.Use(graphql.WithOptions(graphql.Options{
    MaxDepth:      5,
    MaxComplexity: 50,
}))

Production Configuration

// Production-ready settings with introspection disabled
app.Use(graphql.Production())

Disable Introspection

// Block __schema and __type queries
app.Use(graphql.NoIntrospection())

Block Sensitive Fields

// Prevent access to specific fields
app.Use(graphql.BlockFields("password", "secret", "apiKey"))

Allowlist Operations

app.Use(graphql.WithOptions(graphql.Options{
    AllowedOperations: []string{"GetUser", "ListPosts", "CreateComment"},
}))

Custom Error Handler

app.Use(graphql.WithOptions(graphql.Options{
    MaxDepth: 10,
    ErrorHandler: func(c *mizu.Ctx, err error) error {
        return c.JSON(http.StatusForbidden, map[string]any{
            "errors": []map[string]string{
                {"message": "Query validation failed", "detail": err.Error()},
            },
        })
    },
}))

API Reference

Functions

// New creates GraphQL validation middleware with default options
func New() mizu.Middleware

// WithOptions creates GraphQL validation middleware with custom options
func WithOptions(opts Options) mizu.Middleware

// MaxDepth creates middleware with a specific max depth
func MaxDepth(depth int) mizu.Middleware

// MaxComplexity creates middleware with a specific max complexity
func MaxComplexity(complexity int) mizu.Middleware

// NoIntrospection creates middleware that disables introspection
func NoIntrospection() mizu.Middleware

// Production creates middleware with production-ready settings
func Production() mizu.Middleware

// BlockFields creates middleware that blocks specific fields
func BlockFields(fields ...string) mizu.Middleware

Error Types

const (
    ErrQueryTooDeep          = "query exceeds maximum depth"
    ErrQueryTooComplex       = "query exceeds maximum complexity"
    ErrIntrospectionDisabled = "introspection queries are disabled"
    ErrOperationNotAllowed   = "operation not allowed"
    ErrBlockedField          = "query contains blocked field"
)

Technical Details

Query Validation Process

The middleware validates GraphQL queries through the following process:
  1. Request Filtering: Only processes POST requests with application/json content type
  2. Query Parsing: Parses the request body into a Query structure containing:
    • query: The GraphQL query string
    • operationName: Named operation identifier
    • variables: Query variables map
  3. Validation Chain: Applies validations in order:
    • Introspection check (if enabled)
    • Operation allowlist check (if configured)
    • Depth calculation and validation
    • Complexity calculation and validation
    • Blocked fields check (if configured)
  4. Body Restoration: Restores the request body for downstream handlers

Depth Calculation

Query depth is calculated by counting the maximum nesting level of braces {}:
  • Tracks current depth using a counter
  • Increments on {, decrements on }
  • Records the maximum depth reached
Example:
{ users { posts { id } } }  // Depth: 3

Complexity Calculation

Query complexity is estimated using:
  • Count of field selections with braces: \w+\s*(?:\([^)]*\))?\s*{
  • Plus total count of opening braces {
This provides a simple heuristic for query cost.

Introspection Detection

Introspection queries are detected using regex pattern matching for:
  • __schema - Schema introspection
  • __type - Type introspection

Implementation Notes

  • The middleware preserves the request body after validation for downstream handlers
  • Non-POST requests and non-JSON requests bypass validation
  • Failed JSON parsing allows the request to pass through (handled by GraphQL server)
  • Default limits provide reasonable protection for most applications

Best Practices

  • Disable introspection in production
  • Set appropriate depth limits based on your schema
  • Use complexity limits to prevent resource exhaustion
  • Block sensitive fields at the middleware level
  • Implement custom error handlers for better user experience
  • Combine with rate limiting for additional protection

Testing

Test Coverage

Test CaseDescriptionExpected Behavior
TestNewDefault middleware creationAccepts valid queries with default limits
TestWithOptions_MaxDepthQuery depth validationRejects queries exceeding max depth (4 > 2)
TestWithOptions_MaxComplexityQuery complexity validationRejects queries exceeding max complexity
TestWithOptions_DisableIntrospectionIntrospection blockingBlocks __schema and __type queries
TestWithOptions_BlockedFieldsField blockingRejects queries containing blocked fields (password, secret)
TestSkipNonPOSTGET request handlingAllows non-POST requests to pass through
TestMaxDepthMaxDepth helper functionAccepts queries within depth limit (2 ≀ 3)
TestMaxComplexityMaxComplexity helper functionAccepts queries within complexity limit
TestNoIntrospectionNoIntrospection helperBlocks __type introspection queries
TestProductionProduction configurationApplies production settings (depth:10, complexity:100, no introspection)
TestBlockFieldsBlockFields helperBlocks specified mutation fields (deleteMutation, adminAccess)
TestCustomErrorHandlerCustom error handlingUses custom error handler with HTTP 403 status

Security Considerations

Query Depth Attacks

Deep nested queries can cause exponential computation:
{ users { friends { friends { friends { ... } } } } }
Mitigation: Set MaxDepth based on your schema’s maximum legitimate nesting.

Query Complexity Attacks

Complex queries can exhaust server resources:
{ users { posts { comments { author { posts { comments { ... } } } } } } }
Mitigation: Use MaxComplexity to limit total query cost.

Introspection Leaks

Introspection exposes your entire schema structure:
{ __schema { types { name fields { name } } } }
Mitigation: Disable introspection in production with NoIntrospection() or Production().

Sensitive Field Access

Prevent access to sensitive fields:
{ users { password ssn apiKey } }
Mitigation: Use BlockFields() to prevent access to sensitive data.