Skip to main content

Single-target queries

When writing GraphQL queries for transformations, generators, artifacts, and computed attributes, it's critical to use a single-target query pattern to ensure proper tracking by the system.

What is a single-target query?​

A single-target query is a GraphQL query that targets a unique node using a unique attribute or ID. This pattern enables Infrahub to identify exactly which objects are affected by a change, allowing it to selectively trigger the necessary action instead of everything.

Why is this important?​

Without single-target queries, Infrahub cannot determine which specific actions need to be triggered when data changes. This can lead to excessive processing that significantly impacts performance.

Real-world impact: In one production scenario, a proposed change pipeline generated 600 artifact regenerations when only 5 actually required execution. Properly using single-target queries resolved this issue.

Requirements for a valid single-target query​

For a query to be recognized as single-target, it must meet all of these criteria:

  1. Filter on a unique identifier: Use either id or a unique attribute like name__value
  2. Use a required variable: The filter must use a required variable, for example, $name: String!. A literal value is also valid but limits the query to a single fixed object
  3. Use exact match filters: Use singular filters, for example, name__value: $name, not list filters, for example, name__values: $name

Valid single-target query examples​

Using a unique attribute with required variable:

query DeviceConfig($device_name: String!) {
InfraDevice(name__value: $device_name) {
edges {
node {
id
name {
value
}
interfaces {
edges {
node {
name {
value
}
}
}
}
}
}
}
}

Using ID with required variable:

query DeviceById($device_id: String!) {
InfraDevice(ids: [$device_id]) {
edges {
node {
id
name {
value
}
}
}
}
}

Invalid query examples (will cause excessive artifact generation)​

Missing filter (queries all objects):

query AllDevices {
InfraDevice {
edges {
node {
id
name {
value
}
}
}
}
}

Using optional variable:

query DeviceConfig($device_name: String) { # NOT required (no !)
InfraDevice(name__value: $device_name) {
edges {
node {
id
}
}
}
}

Using list filter instead of exact match:

query DeviceConfig($device_name: String!) {
InfraDevice(name__values: $device_name) { # name__values instead of name__value
edges {
node {
id
}
}
}
}

Filtering on non-unique attribute:

query DevicesByRole($role: String!) {
InfraDevice(role__value: $role) { # role is not unique
edges {
node {
id
}
}
}
}

Part of the query is not unique:

query DeviceConfig($device_name: String!) {
InfraDevice(name__value: $device_name) {
edges {
node {
id
name {
value
}
}
}
}
BuiltinTag { # This part is not unique
edges {
node {
name {
value
}
}
}
}
}

Ensuring your query is single-target​

Rather than reviewing a query against the criteria above by hand, ask Infrahub how it will interpret it. Run infrahubctl graphql query-report with the name of the query as declared under queries in your .infrahub.yml:

infrahubctl graphql query-report device_config_query
Query 'device_config_query' (local: queries/device_config.gql)
Targets unique nodes: true

Targets unique nodes: true means the query is single-target. false means it is not, so whatever the query drives runs for every target instead of only the ones that changed.

The report comes from the server, which analyzes the query exactly as the pipeline does, so the two cannot disagree. Two options are worth knowing:

  • --online analyzes the version already loaded into Infrahub instead of the file in your working copy, which is how you check a query that is already deployed.
  • --branch analyzes the query against a specific branch. Uniqueness constraints come from the schema, so a query can be single-target on one branch and not on another.

Infrahub also flags the problem after the fact. When a proposed change pipeline works out what to regenerate for an artifact definition or a Generator definition and cannot map a change to specific targets, it records a warning in the pipeline's task log:

Artifact definition device-config query does not guarantee unique targets. All targets will be processed.

This warning appears only when a relevant field actually changed. A pipeline that touches nothing the query reads stays quiet whether or not the query is single-target, so the absence of a warning in one pipeline does not mean the query is fine. Checking with query-report is the reliable answer.

When single-target queries are required​

Single-target queries are required for:

When single-target queries are NOT required​

You do not need single-target queries for:

  • Ad-hoc queries via the GraphQL interface or API
  • Reporting queries that intentionally fetch multiple objects
  • Dashboard queries for UI components
  • Bulk data exports

In these cases, you can freely query multiple objects without unique filters.