Using Query Cost Analysis

The most exciting part of this library is query cost analysis. It calculates the complexity of queries received and halt execution if the complexity exceeds the permitted value.

Cost analysis works with simple queries, queries with fragments, and mutations.

See also

For more comprehensive usage, check out Advanced Usage of Query Cost Analysis.

Step 1: Build Schema with Cost Directive

First, you need a custom GraphQL directive: @cost() provided by the library.

You can:

  1. import them from `graphql-utilities`, then create a schema along with the directive.

    from graphql import build_schema
    from graphql_utilities import cost_directive_source_doc
    
    build_schema(source=cost_directive_source_doc + """
        type Post @cost(complexity: 5) {
            postId: ID!
            title: String
        }
    """)
    
  2. or use a helper function `build_schema_with_cost()`:

    from graphql_utilities import build_schema_with_cost
    
    build_schema_with_cost(source="""
        type Post @cost(complexity: 5) {
            postId: ID!
            title: String
        }
    """)
    

build_schema_with_cost() is a wrapper of graphql.build_schema(). It stitches the cost directive with your schema before invoking build_schema(). Hence, the function signature of graphql_utilities.build_schema_with_cost() is exactly the same as graphql.build_schema():

1
2
3
4
5
6
def build_schema_with_cost(
        source: Union[str, Source],
        assume_valid=False,
        assume_valid_sdl=False,
        no_location=False,
        experimental_fragment_variables=False):

Step 2: Define Complexity Of Queries/Objects/Fields (Basic Usage)

To define the complexity of a query, simple add a @cost() directive to any Object or Field Definition.

Complexity of Object

To define the complexity of an Object Type, add @cost(complexity: <Int>) directive after the type identifier (post, in the case below).

type Post @cost(complexity: 5) {
    postId: ID!
    title: String
}

type Query {
    post(postId: ID!): Post
}

The complexity of each Post object is 5. The complexity of querying the schema post(postId: ID!) is also 5.

Complexity of Field

You can always define field-specific complexity by adding a @cost() directive to a field:

type Post {
    postId: ID!
    title: String
    lastSnapshot: String @cost(complexity: 8)
}

type Query {
    post(postId: ID!): Post
}

The complexity of the query below will be 8.

{
    posts(postId: "XXXXXX") {
        postId
        lastSnapshot
    }
}

Defining Complexity For Both Object and Fields

In situation when complexity needs to be defined in Object as well as specific Field of the Object, such as:

type Post @cost(complexity: 5) {
    postId: ID!
    title: String
    lastSnapshot: String @cost(complexity: 8)
}

The total complexity of querying an Object with the field lastSnapshot will be the sum of complexities defined in both @cost() directives (5 + 8 = 13).

{
    posts(postId: "XXXXXX") {
        postId
        lastSnapshot
    }
}

The cost of the query above is 5 + 8 = 13.

Step 3: Enable Cost Analysis

To enable cost analysis, you must use the ExtendedExecutionContext provided by the library.

All you need to do are:

  1. Pass graphql_utilities.ExtendedExecutionContext as the value of execution_context_class into any of graphql.graphql_sync(), graphql.graphql(), or graphql.execute()

  2. Pass the following context_value:

    {"cost_analysis": {
       "max_complexity": 5   # Maximum complexity allowed
    }}
    

Example:

from graphql_utilities import ExtendedExecutionContext, build_schema_with_cost

schema = build_schema_with_cost(source="""
    type Post @cost(complexity: 5) {
        postId: ID!
        title: String   @cost(complexity: 20)
    }

    type Query {
        post(postId: ID!): Post
    }
""")

query = """
    {
        post(postId: "XXXXX") {
            postId
            title
        }
    }
"""

results = graphql_sync(schema=schema,
                    source=query,
                    execution_context_class=ExtendedExecutionContext,
                    context_value={
                        "cost_analysis": {
                            "max_complexity": 8
                        }
                    })

results # ExecutionResult(data=None, errors=[GraphQLError("25 exceeded max complexity of 8")])

Advanced Usage

For more advanced usage of query cost analysis, refer to Advanced Usage of Query Cost Analysis.