Skip to content

Conversation

@kasparsklavins
Copy link
Contributor

@kasparsklavins kasparsklavins commented Dec 30, 2025

Fixes #785, test shamelessly taken from #1350

QueryComplexity collects all field nodes and their definitions using a NodeKind::SELECTION_SET visitor. When NodeKind::OPERATION_DEFINITION visitor was run, complexity function, on the field's definition, for each found field was executed.

However, that's not enough when using fragments. When NodeKind::OPERATION_DEFINITION is run, field definitions for later-defined fragments have not yet been collected:

query {
  ...exampleFragment # <-- NodeKind::SELECTION_SET visitor collects definition for only `example`
} # <-- NodeKind::OPERATION_DEFINITION visitor

fragment exampleFragment on Query {
  example {
    a # not visited
    b # not visited 
  }
}

Complexity function for non-collected fields (a and b, in this case) was not run, and a fallback of value 1 was always used instead.


This PR changes QueryComplexity to use a NodeKind::DOCUMENT visitor in place of NodeKind::OPERATION_DEFINITION, as by then it's guaranteed all selection sets have been visited, and field definitions collected.

There is a minor behaviour change with this - when query complexity is exceeded, an error will now be thrown later during validation than before. As error order is not part of the api contract, I assume this is acceptable.

Comment on lines 87 to 103
foreach ($document->definitions as $definition) {
if (! $definition instanceof OperationDefinitionNode) {
continue;
}

$this->queryComplexity = $this->fieldComplexity($definition->selectionSet);

if ($this->queryComplexity > $this->maxQueryComplexity) {
$context->reportError(
new Error(static::maxQueryComplexityErrorMessage(
$this->maxQueryComplexity,
$this->queryComplexity
))
);
return;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part does look weird, but it matches the previous behaviour when a query contains multiple operations:

  1. complexity for all operations is computed
  2. $this->queryComplexity contains complexity of the first operation exceeding the limit.
query A {
  potato
}

query B {
  tomato
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's document this status quo by covering it with tests and maybe a comment to the property. It does seem weird and non-ideal though, sow could we change this in a future major version where we allow breaking changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test for the current behaviour 0f2926f

The validator could be changed to only validate the operation being run; but thats a separate issue and outside the scope of this PR

Dominik Meyer and others added 2 commits December 30, 2025 11:58
When NodeKind::OPERATION_DEFINITION visitor is run, selection sets for later-defined fragments have not yet been colected by NodeKind::SELECTION_SET visitor. Using NodeKind::DOCUMENT visitor instead, guarantees all selection sets have been collected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Query complexity is missing on fields included by fragments

2 participants