graphql-complexity 1.0.0
A python library that provides complexity calculation helpers for GraphQL
License: MIT
Requires Python: <4.0,>=3.10
Keywords: graphql, complexity, query, mutation, subscription

[![GraphQL Complexity Logo](https://github.com/Checho3388/graphql-complexity/raw/main/.github/logo.png)](https://github.com/Checho3388/graphql-complexity/raw/main/.github/logo.png)

# GraphQL Complexity

Welcome to GraphQL-Complexity! This Python library provides functionality to compute the complexity of a GraphQL operation, contributing to better understanding and optimization of your GraphQL APIs. This library is designed to be stable, robust, and highly useful for developers working with GraphQL.

[![Build](https://github.com/Checho3388/graphql-complexity/actions/workflows/python-build.yml/badge.svg)](https://github.com/Checho3388/graphql-complexity/actions/workflows/python-build.yml/badge.svg)
[![PyPI](https://img.shields.io/pypi/v/graphql-complexity?label=pypi%20package)](https://pypi.org/project/graphql-complexity/)
[![codecov](https://codecov.io/gh/Checho3388/graphql-complexity/graph/badge.svg?token=4LH7AVN119)](https://codecov.io/gh/Checho3388/graphql-complexity)
[![Downloads](https://static.pepy.tech/badge/graphql-complexity)](https://pepy.tech/project/graphql-complexity)
[![Python Version](https://img.shields.io/pypi/pyversions/graphql-complexity)](https://pypi.org/project/graphql-complexity/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## ✨ Why GraphQL Complexity?

Protect your GraphQL API from expensive queries and potential DoS attacks by calculating query complexity before execution.

Watch the library live in our demo playground [here](https://graphql-complexity-playground-production.up.railway.app/) .

## Features

* Compute complexity of GraphQL queries
* Multiple built-in estimators for complexity computation
* Customizable estimators for specific use cases
* Support for Strawberry GraphQL library

## Table of Contents

* [Installation](#installation-quick-start)
* [Getting Started](#getting-started)
* [Estimators](#estimators)
  * [SimpleEstimator](#simpleestimator)
  * [DirectivesEstimator](#directivesestimator)
  * [ArgumentsEstimator](#argumentsestimator)
  * [Custom Estimator](#custom-estimator)
* [Advanced Examples](#advanced-examples)
  * [Handling Fragments](#handling-fragments)
  * [Complex Nested Queries](#complex-nested-queries)
  * [Working with Arguments](#working-with-arguments)
  * [Combining Multiple Estimators](#combining-multiple-estimators)
* [Framework Integration](#framework-integration)
  * [Strawberry GraphQL](#strawberry-graphql)
  * [Django Integration](#django-integration)
  * [FastAPI Integration](#fastapi-integration)
  * [Flask Integration](#flask-integration)
* [Real-World Use Cases](#real-world-use-cases)
* [Credits](#credits)

## Installation (Quick Start)

You can install the library via pip:

```bash
pip install graphql-complexity
```

For Strawberry GraphQL integration, use the following command:

```bash
pip install graphql-complexity[strawberry-graphql]
```

## Getting Started

Create a file named `complexity.py` with the following content:

```python
from graphql_complexity import get_complexity, SimpleEstimator
from graphql import build_schema


schema = build_schema("""
    type User {
        id: ID!
        name: String!
    }
    type Query {
        user: User
    }
""")

query = """
    query SomeQuery {
        user {
            id
            name
        }
    }
"""

complexity = get_complexity(
    query=query, 
    schema=schema,
    estimator=SimpleEstimator(complexity=10)
)
if complexity > 10:
    raise Exception("Query is too complex")
```

The library exposes the method `get_complexity` with the algorithm to compute the complexity of a GraphQL operation.
The algorithm visits each node of the query and computes the complexity of each field using an **estimator**.

### Understanding Complexity Calculations with `explain_complexity`

The library provides an `explain_complexity` function that offers detailed insights into how complexity is calculated. This debugging tool returns the inner details that led to the final complexity value, making it invaluable for:

- **Debugging expensive queries** - Identify which fields contribute most to complexity
- **Comparing different queries** - Understand why one query is more expensive than another
- **Learning the library** - See exactly how the estimator evaluates each field
- **Tuning estimators** - Fine-tune your complexity configuration

```python
from graphql_complexity import explain_complexity, SimpleEstimator
from graphql import build_schema

schema = build_schema("""
    type User {
        id: ID!
        name: String!
        posts: [Post!]!
    }
    type Post {
        id: ID!
        title: String!
    }
    type Query {
        user: User
    }
""")

query = """
    query {
        user {
            id
            name
            posts {
                id
                title
            }
        }
    }
"""

explanation = explain_complexity(
    query=query,
    schema=schema,
    estimator=SimpleEstimator(complexity=5)
)

print(explanation)
```

**Example Output:**

```
================================================================================
GraphQL Complexity Explanation
================================================================================

Total Complexity: 35

Estimator Used:
  Name: SimpleEstimator
  Details:
    complexity_constant: 5

Complexity Tree:
--------------------------------------------------------------------------------
root (RootNode) = 35
	user (Field) = 35
		id (Field) = 5
		name (Field) = 5
		posts (ListField) = 25
			id (Field) = 5
			title (Field) = 5

Field-by-Field Breakdown:
--------------------------------------------------------------------------------
user (Field)
  Field complexity: 5
  Children complexity: 30
  Total: 35

user.id (Field)
  Field complexity: 5
  Children complexity: 0
  Total: 5

user.name (Field)
  Field complexity: 5
  Children complexity: 0
  Total: 5

user.posts (ListField) [multiplier: 1]
  Field complexity: 5
  Children complexity: 10
  Total: 25

user.posts.id (Field)
  Field complexity: 5
  Children complexity: 0
  Total: 5

user.posts.title (Field)
  Field complexity: 5
  Children complexity: 0
  Total: 5

================================================================================
```

The explanation shows:
- **Total Complexity**: The final calculated value
- **Estimator Details**: Which estimator was used and its configuration
- **Complexity Tree**: A hierarchical view of how complexity accumulates
- **Field Breakdown**: Per-field analysis showing how each field contributes to the total

## Estimators

GraphQL-Complexity provides various built-in estimators for computing query complexity:

### SimpleEstimator

Estimate fields complexity based on constants for complexity and multiplier. This assigns a constant
complexity value to each field and multiplies it by another constant, which is propagated along the depth of the query.

```python
from graphql_complexity import SimpleEstimator


estimator = SimpleEstimator(complexity=2)
```

### DirectivesEstimator

Define fields complexity using schema directives. This assigns a complexity value to each field and multiplies it
by the depth of the query. It also supports the @complexity directive to assign a custom complexity value to a field.

```python
from graphql_complexity import DirectivesEstimator


schema = """
directive @complexity(
  value: Int!
) on FIELD_DEFINITION

type Query {
  oneField: String @complexity(value: 5)
  otherField: String @complexity(value: 1)
  withoutDirective: String
}
"""

estimator = DirectivesEstimator(schema)
```

### ArgumentsEstimator

Estimate complexity by multiplying a base value by a numeric argument (e.g. `limit`, `first`, `ids`).
This is useful when your API exposes pagination or batch arguments that directly drive the cost of a field.

- **Int argument** — the integer value is used as the multiplier (`limit: 10` → ×10).
- **List argument** — the length of the list is used as the multiplier (`ids: ["a","b","c"]` → ×3).
- **No matching argument** — the field gets `default_complexity` (multiplier of 1).

```python
from graphql_complexity import ArgumentsEstimator, get_complexity
from graphql import build_schema

schema = build_schema("""
    type Query {
        users(limit: Int): [User!]!
        usersByIds(ids: [ID!]): [User!]!
    }
    type User {
        id: ID!
        name: String!
    }
""")

estimator = ArgumentsEstimator(
    multipliers=["limit", "ids"],
    default_complexity=1,
)

# users(limit: 50) → complexity 50
complexity = get_complexity(
    query="query { users(limit: 50) { id name } }",
    schema=schema,
    estimator=estimator,
)
print(complexity)  # 52  (users=50, id=1, name=1)

# usersByIds(ids: ["a","b","c"]) → complexity 3
complexity = get_complexity(
    query='query { usersByIds(ids: ["a", "b", "c"]) { id } }',
    schema=schema,
    estimator=estimator,
)
print(complexity)  # 4  (usersByIds=3, id=1)
```

### Custom estimator

Custom estimators can be defined to compute the complexity of a field using the `ComplexityEstimator` interface.

```python
from graphql_complexity import ComplexityEstimator


class CustomEstimator(ComplexityEstimator):
    def get_field_complexity(self, node, type_info, path) -> int:
        if node.name.value == "specificField":
            return 100
        return 1
```

## Advanced Examples

### Handling Fragments

GraphQL fragments allow you to reuse query parts. Here's how complexity is calculated for queries with fragments:

```python
from graphql_complexity import get_complexity, SimpleEstimator
from graphql import build_schema

schema = build_schema("""
    type User {
        id: ID!
        name: String!
        email: String!
        posts: [Post!]!
    }
    
    type Post {
        id: ID!
        title: String!
        content: String!
        author: User!
    }
    
    type Query {
        user(id: ID!): User
        users: [User!]!
    }
""")

# Query with named fragment
query = """
    query GetUsers {
        users {
            ...UserFields
            posts {
                ...PostFields
            }
        }
    }
    
    fragment UserFields on User {
        id
        name
        email
    }
    
    fragment PostFields on Post {
        id
        title
        content
    }
"""

complexity = get_complexity(
    query=query,
    schema=schema,
    estimator=SimpleEstimator(complexity=1)
)
print(f"Query complexity: {complexity}")
```

### Complex Nested Queries

When dealing with nested relationships, complexity can grow exponentially. Here's how to handle it:

```python
from graphql_complexity import get_complexity, DirectivesEstimator
from graphql import build_schema

schema = build_schema("""
    directive @complexity(
        value: Int!
    ) on FIELD_DEFINITION
    
    type Organization {
        id: ID!
        name: String!
        teams: [Team!]! @complexity(value: 5)
    }
    
    type Team {
        id: ID!
        name: String!
        members: [User!]! @complexity(value: 3)
    }
    
    type User {
        id: ID!
        name: String!
        email: String!
        tasks: [Task!]! @complexity(value: 2)
    }
    
    type Task {
        id: ID!
        title: String!
        description: String!
    }
    
    type Query {
        organization(id: ID!): Organization
    }
""")

# Deeply nested query
query = """
    query GetOrgStructure {
        organization(id: "1") {
            id
            name
            teams {
                id
                name
                members {
                    id
                    name
                    email
                    tasks {
                        id
                        title
                        description
                    }
                }
            }
        }
    }
"""

estimator = DirectivesEstimator(schema)
complexity = get_complexity(query=query, schema=schema, estimator=estimator)

# Set a reasonable limit
MAX_COMPLEXITY = 100
if complexity > MAX_COMPLEXITY:
    raise Exception(f"Query too complex: {complexity} > {MAX_COMPLEXITY}")
```

### Working with Arguments

Field arguments can significantly impact query complexity, especially with pagination. The built-in `ArgumentsEstimator` handles the common case where a numeric argument (like `first` or `limit`) or a list argument (like `ids`) directly drives the cost of a field:

```python
from graphql_complexity import ArgumentsEstimator, get_complexity
from graphql import build_schema

schema = build_schema("""
    type Product {
        id: ID!
        name: String!
        price: Float!
    }

    type Query {
        products(first: Int, offset: Int): [Product!]!
        product(id: ID!): Product
    }
""")

estimator = ArgumentsEstimator(multipliers=["first"], default_complexity=1)

query = """
    query GetProducts {
        products(first: 50) {
            id
            name
            price
        }
    }
"""

complexity = get_complexity(query=query, schema=schema, estimator=estimator)
print(f"Query complexity with pagination: {complexity}")
# products(first: 50) → 50, id/name/price → 1 each → total: 53
```

For more fine-grained control (e.g. capping limits or applying field-specific multipliers), use a [Custom Estimator](#custom-estimator) instead.

### Combining Multiple Estimators

You can create sophisticated complexity analysis by combining estimators:

```python
from graphql_complexity import (
    ComplexityEstimator,
    SimpleEstimator,
    DirectivesEstimator,
    get_complexity
)
from graphql import build_schema

schema = build_schema("""
    directive @complexity(value: Int!) on FIELD_DEFINITION
    
    type User {
        id: ID!
        name: String!
        expensiveComputation: String! @complexity(value: 50)
        posts: [Post!]!
    }
    
    type Post {
        id: ID!
        title: String!
    }
    
    type Query {
        user(id: ID!): User
        users: [User!]!
    }
""")

class CompositeEstimator(ComplexityEstimator):
    """Combines multiple estimation strategies"""
    
    def __init__(self):
        self.directive_estimator = DirectivesEstimator(schema)
        self.simple_estimator = SimpleEstimator(complexity=1)
    
    def get_field_complexity(self, node, type_info, path) -> int:
        # First, try directive-based estimation
        directive_complexity = self.directive_estimator.get_field_complexity(
            node, type_info, path
        )
        
        # If directive provides a value, use it
        if directive_complexity > 1:
            return directive_complexity
        
        # Otherwise, fall back to simple estimation
        return self.simple_estimator.get_field_complexity(node, type_info, path)

query = """
    query GetUserData {
        user(id: "1") {
            id
            name
            expensiveComputation
            posts {
                id
                title
            }
        }
    }
"""

complexity = get_complexity(
    query=query,
    schema=schema,
    estimator=CompositeEstimator()
)
print(f"Combined estimator complexity: {complexity}")
```

## Framework Integration

### Strawberry GraphQL

The library is compatible with [strawberry-graphql](https://pypi.org/project/strawberry-graphql/).
Use the following command to install the library with Strawberry support:

```bash
poetry install --extras strawberry-graphql
```

To use the library with Strawberry GraphQL, use the `build_complexity_extension` method to build the complexity
extension and add it to the schema. This method receives an estimator and returns a complexity extension that can be added to the schema.

```python
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())
schema = strawberry.Schema(query=Query, extensions=[extension])

schema.execute_sync("query { helloWorld }")
```

The `build_complexity_extension` method accepts an estimator as optional argument giving the possibility to use one
of the built-in estimators or a custom estimator.

### Django Integration

Integrate complexity analysis with Django and Graphene:

```python
# 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):
        # Calculate complexity on first field resolution
        if not hasattr(info.context, '_complexity_checked'):
            try:
                complexity = get_complexity(
                    query=info.operation.loc.source.body,
                    schema=info.schema,
                    estimator=self.estimator
                )
                
                if complexity > self.max_complexity:
                    raise GraphQLError(
                        f"Query is too complex: {complexity}. "
                        f"Maximum allowed complexity: {self.max_complexity}"
                    )
                
                info.context._complexity_checked = True
                info.context._query_complexity = complexity
                
            except Exception as e:
                raise GraphQLError(f"Complexity analysis failed: {str(e)}")
        
        return next(root, info, **args)


# settings.py
GRAPHENE = {
    'MIDDLEWARE': [
        'myapp.complexity.ComplexityMiddleware',
    ],
}
```

### FastAPI Integration

Use complexity analysis with FastAPI and Strawberry:

```python
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"),
        ]


# Configure complexity limit
MAX_COMPLEXITY = 1000
extension = build_complexity_extension(
    estimator=SimpleEstimator(complexity=1),
    max_complexity=MAX_COMPLEXITY
)

schema = strawberry.Schema(query=Query, extensions=[extension])
graphql_app = GraphQLRouter(schema)

app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")
```

### Flask Integration

Integrate with Flask-GraphQL:

```python
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)


def validate_complexity(query_string):
    """Validate query complexity before execution"""
    try:
        complexity = get_complexity(
            query=query_string,
            schema=schema,
            estimator=estimator
        )
        
        if complexity > MAX_COMPLEXITY:
            raise GraphQLError(
                f"Query exceeds complexity limit. "
                f"Query complexity: {complexity}, Max allowed: {MAX_COMPLEXITY}"
            )
        
        return complexity
    except Exception as e:
        raise GraphQLError(f"Complexity validation error: {str(e)}")


class ComplexityGraphQLView(GraphQLView):
    def dispatch_request(self):
        # Get the query from request
        data = request.get_json()
        query = data.get('query', '')
        
        # Validate complexity
        try:
            complexity = validate_complexity(query)
            # Store complexity in request context for logging
            request.query_complexity = complexity
        except GraphQLError as e:
            return jsonify({'errors': [{'message': str(e)}]}), 400
        
        # Proceed with normal GraphQL execution
        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)
```

## 🍳 Recipes

### 1. Reject a query before execution

The most common pattern — block expensive queries before they hit your resolvers.

```python
from graphql_complexity import get_complexity, SimpleEstimator

MAX_COMPLEXITY = 10

def execute_query(schema, query):
    complexity = get_complexity(
        query=query,
        schema=schema,
        estimator=SimpleEstimator(complexity=1)
    )

    if complexity > MAX_COMPLEXITY:
        raise Exception(f"Query too complex ({complexity}). Max allowed: {MAX_COMPLEXITY}")

    return schema.execute(query)
```

-----

### 2. Set different limits per user role

Give trusted users more headroom without opening the door to abuse.

```python
COMPLEXITY_LIMITS = {
    "anonymous": 5,
    "authenticated": 15,
    "admin": 50,
}

def execute_query(schema, query, user_role="anonymous"):
    complexity = get_complexity(
        query=query,
        schema=schema,
        estimator=SimpleEstimator(complexity=1)
    )

    limit = COMPLEXITY_LIMITS.get(user_role, 5)

    if complexity > limit:
        raise Exception(f"Query too complex ({complexity}). Max allowed for {user_role}: {limit}")

    return schema.execute(query)
```

-----

### 3. Log complexity without blocking

Useful when rolling out complexity limits gradually — monitor first, enforce later.

```python
import logging
from graphql_complexity import get_complexity, SimpleEstimator

logger = logging.getLogger(__name__)
WARN_THRESHOLD = 10

def execute_query(schema, query):
    complexity = get_complexity(
        query=query,
        schema=schema,
        estimator=SimpleEstimator(complexity=1)
    )

    if complexity > WARN_THRESHOLD:
        logger.warning(f"High complexity query detected: {complexity}")

    # Always execute — no blocking yet
    return schema.execute(query)
```

> **Tip:** Use this pattern in production for a week to understand your traffic’s complexity distribution before choosing your enforcement threshold.

## Real-World Use Cases

### E-commerce Platform

Protect your e-commerce API from expensive queries:

```python
from graphql_complexity import ComplexityEstimator, get_complexity
from graphql import build_schema

schema = build_schema("""
    directive @complexity(value: Int!) on FIELD_DEFINITION
    
    type Product {
        id: ID!
        name: String!
        description: String!
        price: Float!
        reviews: [Review!]! @complexity(value: 10)
        relatedProducts: [Product!]! @complexity(value: 15)
    }
    
    type Review {
        id: ID!
        rating: Int!
        comment: String!
        author: User!
    }
    
    type User {
        id: ID!
        name: String!
        orders: [Order!]! @complexity(value: 20)
    }
    
    type Order {
        id: ID!
        items: [Product!]!
        total: Float!
    }
    
    type Query {
        product(id: ID!): Product
        products(limit: Int, offset: Int): [Product!]!
    }
""")

# This query would be too expensive
expensive_query = """
    query ExpensiveQuery {
        products(limit: 100) {
            id
            name
            reviews {
                author {
                    orders {
                        items {
                            relatedProducts {
                                reviews {
                                    comment
                                }
                            }
                        }
                    }
                }
            }
        }
    }
"""

# Reasonable query
reasonable_query = """
    query ReasonableQuery {
        product(id: "123") {
            id
            name
            price
            description
        }
    }
"""
```

### Social Media API

Rate limit complex social graph queries:

```python
from graphql_complexity import ComplexityEstimator, get_complexity
from graphql import build_schema

schema = build_schema("""
    directive @complexity(value: Int!) on FIELD_DEFINITION
    
    type User {
        id: ID!
        username: String!
        followers: [User!]! @complexity(value: 10)
        following: [User!]! @complexity(value: 10)
        posts: [Post!]! @complexity(value: 5)
    }
    
    type Post {
        id: ID!
        content: String!
        author: User!
        likes: [User!]! @complexity(value: 5)
        comments: [Comment!]! @complexity(value: 3)
    }
    
    type Comment {
        id: ID!
        text: String!
        author: User!
    }
    
    type Query {
        user(id: ID!): User
        feed(limit: Int): [Post!]!
    }
""")


class SocialMediaEstimator(ComplexityEstimator):
    """Custom estimator for social media queries"""
    
    def get_field_complexity(self, node, type_info, path) -> int:
        field_name = node.name.value
        
        # Heavy penalty for deeply nested social graphs
        depth = len(path)
        depth_penalty = 2 ** depth if depth > 3 else 1
        
        # Base costs
        costs = {
            'followers': 10,
            'following': 10,
            'posts': 5,
            'likes': 5,
            'comments': 3,
            'feed': 8,
        }
        
        base_cost = costs.get(field_name, 1)
        return base_cost * depth_penalty


# Example: Preventing graph explosion
problematic_query = """
    query DeepSocialGraph {
        user(id: "1") {
            followers {
                following {
                    posts {
                        likes {
                            followers {
                                # This gets exponentially expensive!
                                username
                            }
                        }
                    }
                }
            }
        }
    }
"""
```

### Analytics Dashboard

Manage complexity for data-heavy analytics queries:

```python
from graphql_complexity import ComplexityEstimator, get_complexity
from graphql import build_schema
from datetime import datetime

schema = build_schema("""
    directive @complexity(value: Int!) on FIELD_DEFINITION
    
    type Analytics {
        pageViews: Int! @complexity(value: 5)
        uniqueVisitors: Int! @complexity(value: 5)
        averageSessionDuration: Float! @complexity(value: 10)
        conversionRate: Float! @complexity(value: 15)
        revenueByDay: [DailyRevenue!]! @complexity(value: 20)
    }
    
    type DailyRevenue {
        date: String!
        amount: Float!
        transactions: Int!
    }
    
    type Query {
        analytics(
            startDate: String!
            endDate: String!
            granularity: String
        ): Analytics @complexity(value: 10)
    }
""")


def calculate_analytics_complexity(query: str, date_range_days: int) -> int:
    """Calculate complexity based on date range"""
    base_complexity = get_complexity(
        query=query,
        schema=schema,
        estimator=SimpleEstimator(complexity=1)
    )
    
    # Multiply by date range (more days = more expensive)
    range_multiplier = max(1, date_range_days / 7)  # Weekly baseline
    
    return int(base_complexity * range_multiplier)
```

## Credits

Estimators idea was heavily inspired by [graphql-query-complexity](https://github.com/slicknode/graphql-query-complexity).

## About

Python library to compute the complexity of a GraphQL operation

### Topics

[python](https://github.com/topics/python)
[graphql](https://github.com/topics/graphql)
[strawberry](https://github.com/topics/strawberry)
[graphql-core](https://github.com/topics/graphql-core)

### License

[MIT license](LICENSE)