Framework Integrations¶
graphql-complexity is framework-agnostic — it works with any GraphQL server by wrapping
get_complexity around your execution pipeline. This page shows how to wire it up with the
most common Python frameworks.
Strawberry GraphQL¶
Strawberry has first-class support via a schema extension. Install the extra first:
pip install graphql-complexity[strawberry-graphql]
Use build_complexity_extension to create the extension and attach it to your schema:
import strawberry
from graphql_complexity import SimpleEstimator
from graphql_complexity.extensions.strawberry_graphql import build_complexity_extension
@strawberry.type
class Query:
@strawberry.field
def hello_world(self) -> str:
return "Hello world!"
extension = build_complexity_extension(
estimator=SimpleEstimator(complexity=1),
max_complexity=100,
)
schema = strawberry.Schema(query=Query, extensions=[extension])
schema.execute_sync("query { helloWorld }")
When a query exceeds max_complexity the extension adds an error to the response and
prevents execution — no resolver is ever called.
build_complexity_extension reference¶
build_complexity_extension(
estimator: ComplexityEstimator = SimpleEstimator(),
max_complexity: int | None = None,
) -> type[SchemaExtension]
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Estimator used to score fields |
|
|
|
Reject queries above this score. |
Django¶
With Graphene-Django, the cleanest integration point is a custom middleware class that intercepts each request and checks complexity before any resolver runs.
# myapp/complexity.py
from graphql_complexity import get_complexity, SimpleEstimator
from graphql import GraphQLError
class ComplexityMiddleware:
def __init__(self, max_complexity=1000):
self.max_complexity = max_complexity
self.estimator = SimpleEstimator(complexity=1)
def resolve(self, next, root, info, **args):
if not hasattr(info.context, '_complexity_checked'):
complexity = get_complexity(
query=info.operation.loc.source.body,
schema=info.schema,
estimator=self.estimator,
)
if complexity > self.max_complexity:
raise GraphQLError(
f"Query too complex: {complexity}. "
f"Maximum allowed: {self.max_complexity}"
)
info.context._complexity_checked = True
info.context._query_complexity = complexity
return next(root, info, **args)
Register it in settings.py:
# settings.py
GRAPHENE = {
"MIDDLEWARE": [
"myapp.complexity.ComplexityMiddleware",
],
}
The _complexity_checked flag on info.context ensures the check only runs once per request
even though resolve is called for every field.
FastAPI¶
With Strawberry’s FastAPI router, you can reuse the same build_complexity_extension approach
as in the Strawberry section above:
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
import strawberry
from graphql_complexity import SimpleEstimator
from graphql_complexity.extensions.strawberry_graphql import build_complexity_extension
@strawberry.type
class User:
id: strawberry.ID
name: str
email: str
@strawberry.type
class Query:
@strawberry.field
def user(self, id: strawberry.ID) -> User:
return User(id=id, name="John Doe", email="john@example.com")
@strawberry.field
def users(self) -> list[User]:
return [
User(id="1", name="John Doe", email="john@example.com"),
User(id="2", name="Jane Smith", email="jane@example.com"),
]
extension = build_complexity_extension(
estimator=SimpleEstimator(complexity=1),
max_complexity=1000,
)
schema = strawberry.Schema(query=Query, extensions=[extension])
app = FastAPI()
app.include_router(GraphQLRouter(schema), prefix="/graphql")
Flask¶
With Flask-GraphQL, subclass GraphQLView and validate complexity in dispatch_request
before delegating to the standard execution:
from flask import Flask, request, jsonify
from flask_graphql import GraphQLView
from graphql import build_schema, GraphQLError
from graphql_complexity import get_complexity, SimpleEstimator
app = Flask(__name__)
schema = build_schema("""
type User {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
users: [User!]!
}
""")
MAX_COMPLEXITY = 1000
estimator = SimpleEstimator(complexity=1)
class ComplexityGraphQLView(GraphQLView):
def dispatch_request(self):
data = request.get_json()
query = data.get("query", "")
try:
complexity = get_complexity(
query=query,
schema=schema,
estimator=estimator,
)
except Exception as e:
return jsonify({"errors": [{"message": f"Complexity analysis failed: {e}"}]}), 400
if complexity > MAX_COMPLEXITY:
return jsonify({
"errors": [{
"message": (
f"Query exceeds complexity limit. "
f"Query complexity: {complexity}, max allowed: {MAX_COMPLEXITY}"
)
}]
}), 400
# Store for logging/monitoring
request.query_complexity = complexity
return super().dispatch_request()
app.add_url_rule(
"/graphql",
view_func=ComplexityGraphQLView.as_view(
"graphql",
schema=schema,
graphiql=True,
),
)
if __name__ == "__main__":
app.run(debug=True)
Choosing the right pattern¶
All patterns reject the query before any resolver runs, so your business logic is never reached for over-budget queries.