Repository: opsmill/infrahub-sdk-python

Issue:
feature: Add flag to control the usage or not of property
### Component

Python SDK

### Describe the Feature Request

Currently the Python SDK always include all properties (aka metadata) by default, for both attributes and relationship as part of all queries.
This behavior works great at small scale but it can have a significant performance penalty when we need to retrieve a large number of nodes.

The proposal is to :
- Add a flag to control if the properties should be included in the queries for a given action
- Disable the usage of the properties by default

### Describe the Use Case

Reduce the number of data queried by the SDK by default and improve the performance of the SDK for large dataset

### Additional Information

_No response_

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) Report
Attention: Patch coverage is `89.28571%` with `3 lines` in your changes missing coverage. Please review.

| [Files with missing lines](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | Patch % | Lines |
|---|---|---|
| [infrahub\_sdk/node.py](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199?src=pr&el=tree&filepath=infrahub_sdk%2Fnode.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill#diff-aW5mcmFodWJfc2RrL25vZGUucHk=) | 89.28% | [3 Missing :warning: ](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) |

> :exclamation:  There is a different number of reports uploaded between BASE (f99a19e) and HEAD (af27e6a). Click for more details.
> 
> <details><summary>HEAD has 8 uploads less than BASE</summary>
>
>| Flag | BASE (f99a19e) | HEAD (af27e6a) |
>|------|------|------|
>|python-3.9|2|1|
>|python-3.10|2|1|
>|python-3.12|2|1|
>|python-3.11|2|1|
>|python-filler-3.12|2|0|
>|python-3.13|2|0|
></details>

```diff
@@             Coverage Diff              @@
##           develop     #199       +/-   ##
============================================
- Coverage    65.43%   43.75%   -21.68%     
============================================
  Files           81       81               
  Lines         7259     7263        +4     
  Branches      1409     1410        +1     
============================================
- Hits          4750     3178     -1572     
- Misses        2122     3722     +1600     
+ Partials       387      363       -24     
```

| [Flag](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | Coverage Δ | |
|---|---|---|
| [python-3.10](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | `43.75% <89.28%> (+0.05%)` | :arrow_up: |
| [python-3.11](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | `43.75% <89.28%> (+0.05%)` | :arrow_up: |
| [python-3.12](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | `43.75% <89.28%> (+0.05%)` | :arrow_up: |
| [python-3.13](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | `?` | |
| [python-3.9](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | `42.85% <89.28%> (+0.05%)` | :arrow_up: |
| [python-filler-3.12](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files with missing lines](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill) | Coverage Δ | |
|---|---|---|
| [infrahub\_sdk/client.py](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199?src=pr&el=tree&filepath=infrahub_sdk%2Fclient.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill#diff-aW5mcmFodWJfc2RrL2NsaWVudC5weQ==) | `42.33% <ø> (-20.11%)` | :arrow_down: |
| [infrahub\_sdk/node.py](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199?src=pr&el=tree&filepath=infrahub_sdk%2Fnode.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill#diff-aW5mcmFodWJfc2RrL25vZGUucHk=) | `56.33% <89.28%> (-15.06%)` | :arrow_down: |

... and [40 files with indirect coverage changes](https://app.codecov.io/gh/opsmill/infrahub-sdk-python/pull/199/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opsmill)


Patch:
diff --git a/changelog/191.changed.md b/changelog/191.changed.md
new file mode 100644
index 0000000..b3c0123
--- /dev/null
+++ b/changelog/191.changed.md
@@ -0,0 +1,2 @@
+Disable by default the inclusion of properties for Attribute and Relationships when querying nodes from Infrahub.
+The inclusion of properties can be enabled by using the parameter property in client.get|all|filters
\ No newline at end of file
diff --git a/infrahub_sdk/client.py b/infrahub_sdk/client.py
index 4b56153..3b9004c 100644
--- a/infrahub_sdk/client.py
+++ b/infrahub_sdk/client.py
@@ -331,6 +331,7 @@ class InfrahubClient(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> SchemaType | None: ...
 
@@ -349,6 +350,7 @@ class InfrahubClient(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> SchemaType: ...
 
@@ -367,6 +369,7 @@ class InfrahubClient(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> SchemaType: ...
 
@@ -385,6 +388,7 @@ class InfrahubClient(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> InfrahubNode | None: ...
 
@@ -403,6 +407,7 @@ class InfrahubClient(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> InfrahubNode: ...
 
@@ -421,6 +426,7 @@ class InfrahubClient(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> InfrahubNode: ...
 
@@ -438,6 +444,7 @@ class InfrahubClient(BaseClient):
         populate_store: bool = False,
         fragment: bool = False,
         prefetch_relationships: bool = False,
+        property: bool = False,
         **kwargs: Any,
     ) -> InfrahubNode | SchemaType | None:
         branch = branch or self.default_branch
@@ -470,6 +477,7 @@ class InfrahubClient(BaseClient):
             exclude=exclude,
             fragment=fragment,
             prefetch_relationships=prefetch_relationships,
+            property=property,
             **filters,
         )
 
@@ -533,6 +541,7 @@ class InfrahubClient(BaseClient):
         exclude: list[str] | None = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
     ) -> list[SchemaType]: ...
 
     @overload
@@ -549,6 +558,7 @@ class InfrahubClient(BaseClient):
         exclude: list[str] | None = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
     ) -> list[InfrahubNode]: ...
 
     async def all(
@@ -564,6 +574,7 @@ class InfrahubClient(BaseClient):
         exclude: list[str] | None = None,
         fragment: bool = False,
         prefetch_relationships: bool = False,
+        property: bool = False,
     ) -> list[InfrahubNode] | list[SchemaType]:
         """Retrieve all nodes of a given kind
 
@@ -595,6 +606,7 @@ class InfrahubClient(BaseClient):
             exclude=exclude,
             fragment=fragment,
             prefetch_relationships=prefetch_relationships,
+            property=property,
         )
 
     @overload
@@ -612,6 +624,7 @@ class InfrahubClient(BaseClient):
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
         partial_match: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> list[SchemaType]: ...
 
@@ -630,6 +643,7 @@ class InfrahubClient(BaseClient):
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
         partial_match: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> list[InfrahubNode]: ...
 
@@ -647,6 +661,7 @@ class InfrahubClient(BaseClient):
         fragment: bool = False,
         prefetch_relationships: bool = False,
         partial_match: bool = False,
+        property: bool = False,
         **kwargs: Any,
     ) -> list[InfrahubNode] | list[SchemaType]:
         """Retrieve nodes of a given kind based on provided filters.
@@ -696,6 +711,7 @@ class InfrahubClient(BaseClient):
                 fragment=fragment,
                 prefetch_relationships=prefetch_relationships,
                 partial_match=partial_match,
+                # property=property,
             )
             query = Query(query=query_data)
             response = await self.execute_graphql(
@@ -1549,6 +1565,7 @@ class InfrahubClientSync(BaseClient):
         exclude: list[str] | None = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
     ) -> list[SchemaTypeSync]: ...
 
     @overload
@@ -1565,6 +1582,7 @@ class InfrahubClientSync(BaseClient):
         exclude: list[str] | None = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
     ) -> list[InfrahubNodeSync]: ...
 
     def all(
@@ -1580,6 +1598,7 @@ class InfrahubClientSync(BaseClient):
         exclude: list[str] | None = None,
         fragment: bool = False,
         prefetch_relationships: bool = False,
+        property: bool = False,
     ) -> list[InfrahubNodeSync] | list[SchemaTypeSync]:
         """Retrieve all nodes of a given kind
 
@@ -1611,6 +1630,7 @@ class InfrahubClientSync(BaseClient):
             exclude=exclude,
             fragment=fragment,
             prefetch_relationships=prefetch_relationships,
+            property=property,
         )
 
     def _process_nodes_and_relationships(
@@ -1663,6 +1683,7 @@ class InfrahubClientSync(BaseClient):
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
         partial_match: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> list[SchemaTypeSync]: ...
 
@@ -1681,6 +1702,7 @@ class InfrahubClientSync(BaseClient):
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
         partial_match: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> list[InfrahubNodeSync]: ...
 
@@ -1698,6 +1720,7 @@ class InfrahubClientSync(BaseClient):
         fragment: bool = False,
         prefetch_relationships: bool = False,
         partial_match: bool = False,
+        property: bool = False,
         **kwargs: Any,
     ) -> list[InfrahubNodeSync] | list[SchemaTypeSync]:
         """Retrieve nodes of a given kind based on provided filters.
@@ -1747,6 +1770,7 @@ class InfrahubClientSync(BaseClient):
                 fragment=fragment,
                 prefetch_relationships=prefetch_relationships,
                 partial_match=partial_match,
+                property=property,
             )
             query = Query(query=query_data)
             response = self.execute_graphql(
@@ -1799,6 +1823,7 @@ class InfrahubClientSync(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> SchemaTypeSync | None: ...
 
@@ -1817,6 +1842,7 @@ class InfrahubClientSync(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> SchemaTypeSync: ...
 
@@ -1835,6 +1861,7 @@ class InfrahubClientSync(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> SchemaTypeSync: ...
 
@@ -1853,6 +1880,7 @@ class InfrahubClientSync(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> InfrahubNodeSync | None: ...
 
@@ -1871,6 +1899,7 @@ class InfrahubClientSync(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> InfrahubNodeSync: ...
 
@@ -1889,6 +1918,7 @@ class InfrahubClientSync(BaseClient):
         populate_store: bool = ...,
         fragment: bool = ...,
         prefetch_relationships: bool = ...,
+        property: bool = ...,
         **kwargs: Any,
     ) -> InfrahubNodeSync: ...
 
@@ -1906,6 +1936,7 @@ class InfrahubClientSync(BaseClient):
         populate_store: bool = False,
         fragment: bool = False,
         prefetch_relationships: bool = False,
+        property: bool = False,
         **kwargs: Any,
     ) -> InfrahubNodeSync | SchemaTypeSync | None:
         branch = branch or self.default_branch
@@ -1938,6 +1969,7 @@ class InfrahubClientSync(BaseClient):
             exclude=exclude,
             fragment=fragment,
             prefetch_relationships=prefetch_relationships,
+            property=property,
             **filters,
         )
 
diff --git a/infrahub_sdk/node.py b/infrahub_sdk/node.py
index 42d3a0a..ce9865e 100644
--- a/infrahub_sdk/node.py
+++ b/infrahub_sdk/node.py
@@ -123,13 +123,16 @@ class Attribute:
 
         return {"data": data, "variables": variables}
 
-    def _generate_query_data(self) -> dict | None:
-        data: dict[str, Any] = {"value": None, "is_default": None, "is_from_profile": None}
+    def _generate_query_data(self, property: bool = False) -> dict | None:
+        data: dict[str, Any] = {"value": None}
 
-        for prop_name in self._properties_flag:
-            data[prop_name] = None
-        for prop_name in self._properties_object:
-            data[prop_name] = {"id": None, "display_label": None, "__typename": None}
+        if property:
+            data.update({"is_default": None, "is_from_profile": None})
+
+            for prop_name in self._properties_flag:
+                data[prop_name] = None
+            for prop_name in self._properties_object:
+                data[prop_name] = {"id": None, "display_label": None, "__typename": None}
 
         return data
 
@@ -266,7 +269,7 @@ class RelatedNodeBase:
         return {}
 
     @classmethod
-    def _generate_query_data(cls, peer_data: dict[str, Any] | None = None) -> dict:
+    def _generate_query_data(cls, peer_data: dict[str, Any] | None = None, property: bool = False) -> dict:
         """Generates the basic structure of a GraphQL query for a single relationship.
 
         Args:
@@ -278,12 +281,13 @@ class RelatedNodeBase:
                 and typename. The method also includes additional properties and any peer_data provided.
         """
         data: dict[str, Any] = {"node": {"id": None, "hfid": None, "display_label": None, "__typename": None}}
-
         properties: dict[str, Any] = {}
-        for prop_name in PROPERTIES_FLAG:
-            properties[prop_name] = None
-        for prop_name in PROPERTIES_OBJECT:
-            properties[prop_name] = {"id": None, "display_label": None, "__typename": None}
+
+        if property:
+            for prop_name in PROPERTIES_FLAG:
+                properties[prop_name] = None
+            for prop_name in PROPERTIES_OBJECT:
+                properties[prop_name] = {"id": None, "display_label": None, "__typename": None}
 
         if properties:
             data["properties"] = properties
@@ -433,7 +437,7 @@ class RelationshipManagerBase:
         return {}
 
     @classmethod
-    def _generate_query_data(cls, peer_data: dict[str, Any] | None = None) -> dict:
+    def _generate_query_data(cls, peer_data: dict[str, Any] | None = None, property: bool = False) -> dict:
         """Generates the basic structure of a GraphQL query for relationships with multiple nodes.
 
         Args:
@@ -451,10 +455,11 @@ class RelationshipManagerBase:
         }
 
         properties: dict[str, Any] = {}
-        for prop_name in PROPERTIES_FLAG:
-            properties[prop_name] = None
-        for prop_name in PROPERTIES_OBJECT:
-            properties[prop_name] = {"id": None, "display_label": None, "__typename": None}
+        if property:
+            for prop_name in PROPERTIES_FLAG:
+                properties[prop_name] = None
+            for prop_name in PROPERTIES_OBJECT:
+                properties[prop_name] = {"id": None, "display_label": None, "__typename": None}
 
         if properties:
             data["edges"]["properties"] = properties
@@ -1170,6 +1175,7 @@ class InfrahubNode(InfrahubNodeBase):
         fragment: bool = False,
         prefetch_relationships: bool = False,
         partial_match: bool = False,
+        property: bool = False,
     ) -> dict[str, Any | dict]:
         data = self.generate_query_data_init(
             filters=filters, offset=offset, limit=limit, include=include, exclude=exclude, partial_match=partial_match
@@ -1180,6 +1186,7 @@ class InfrahubNode(InfrahubNodeBase):
                 exclude=exclude,
                 prefetch_relationships=prefetch_relationships,
                 inherited=True,
+                property=property,
             )
         )
 
@@ -1202,6 +1209,7 @@ class InfrahubNode(InfrahubNodeBase):
                     prefetch_relationships=prefetch_relationships,
                     inherited=False,
                     insert_alias=True,
+                    property=property,
                 )
 
                 if child_data:
@@ -1216,6 +1224,7 @@ class InfrahubNode(InfrahubNodeBase):
         inherited: bool = True,
         insert_alias: bool = False,
         prefetch_relationships: bool = False,
+        property: bool = False,
     ) -> dict[str, Any | dict]:
         """Generate the node part of a GraphQL Query with attributes and nodes.
 
@@ -1243,7 +1252,7 @@ class InfrahubNode(InfrahubNodeBase):
             if not inherited and attr._schema.inherited:
                 continue
 
-            attr_data = attr._generate_query_data()
+            attr_data = attr._generate_query_data(property=property)
             if attr_data:
                 data[attr_name] = attr_data
                 if insert_alias:
@@ -1272,10 +1281,14 @@ class InfrahubNode(InfrahubNodeBase):
             if rel_schema and prefetch_relationships:
                 peer_schema = await self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
                 peer_node = InfrahubNode(client=self._client, schema=peer_schema, branch=self._branch)
-                peer_data = await peer_node.generate_query_data_node(include=include, exclude=exclude)
+                peer_data = await peer_node.generate_query_data_node(
+                    include=include,
+                    exclude=exclude,
+                    property=property,
+                )
 
             if rel_schema and rel_schema.cardinality == "one":
-                rel_data = RelatedNode._generate_query_data(peer_data=peer_data)
+                rel_data = RelatedNode._generate_query_data(peer_data=peer_data, property=property)
                 # Nodes involved in a hierarchy are required to inherit from a common ancestor node, and graphql
                 # tries to resolve attributes in this ancestor instead of actual node. To avoid
                 # invalid queries issues when attribute is missing in the common ancestor, we use a fragment
@@ -1285,7 +1298,7 @@ class InfrahubNode(InfrahubNodeBase):
                     rel_data["node"] = {}
                     rel_data["node"][f"...on {rel_schema.peer}"] = data_node
             elif rel_schema and rel_schema.cardinality == "many":
-                rel_data = RelationshipManager._generate_query_data(peer_data=peer_data)
+                rel_data = RelationshipManager._generate_query_data(peer_data=peer_data, property=property)
 
             data[rel_name] = rel_data
 
@@ -1668,6 +1681,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
         fragment: bool = False,
         prefetch_relationships: bool = False,
         partial_match: bool = False,
+        property: bool = False,
     ) -> dict[str, Any | dict]:
         data = self.generate_query_data_init(
             filters=filters, offset=offset, limit=limit, include=include, exclude=exclude, partial_match=partial_match
@@ -1678,6 +1692,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
                 exclude=exclude,
                 prefetch_relationships=prefetch_relationships,
                 inherited=True,
+                property=property,
             )
         )
 
@@ -1699,6 +1714,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
                     prefetch_relationships=prefetch_relationships,
                     inherited=False,
                     insert_alias=True,
+                    property=property,
                 )
 
                 if child_data:
@@ -1713,6 +1729,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
         inherited: bool = True,
         insert_alias: bool = False,
         prefetch_relationships: bool = False,
+        property: bool = False,
     ) -> dict[str, Any | dict]:
         """Generate the node part of a GraphQL Query with attributes and nodes.
 
@@ -1740,7 +1757,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
             if not inherited and attr._schema.inherited:
                 continue
 
-            attr_data = attr._generate_query_data()
+            attr_data = attr._generate_query_data(property=property)
             if attr_data:
                 data[attr_name] = attr_data
                 if insert_alias:
@@ -1769,10 +1786,10 @@ class InfrahubNodeSync(InfrahubNodeBase):
             if rel_schema and prefetch_relationships:
                 peer_schema = self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
                 peer_node = InfrahubNodeSync(client=self._client, schema=peer_schema, branch=self._branch)
-                peer_data = peer_node.generate_query_data_node(include=include, exclude=exclude)
+                peer_data = peer_node.generate_query_data_node(include=include, exclude=exclude, property=property)
 
             if rel_schema and rel_schema.cardinality == "one":
-                rel_data = RelatedNodeSync._generate_query_data(peer_data=peer_data)
+                rel_data = RelatedNodeSync._generate_query_data(peer_data=peer_data, property=property)
                 # Nodes involved in a hierarchy are required to inherit from a common ancestor node, and graphql
                 # tries to resolve attributes in this ancestor instead of actual node. To avoid
                 # invalid queries issues when attribute is missing in the common ancestor, we use a fragment
@@ -1782,7 +1799,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
                     rel_data["node"] = {}
                     rel_data["node"][f"...on {rel_schema.peer}"] = data_node
             elif rel_schema and rel_schema.cardinality == "many":
-                rel_data = RelationshipManagerSync._generate_query_data(peer_data=peer_data)
+                rel_data = RelationshipManagerSync._generate_query_data(peer_data=peer_data, property=property)
 
             data[rel_name] = rel_data
 


Test patch:
diff --git a/tests/unit/sdk/test_node.py b/tests/unit/sdk/test_node.py
index 60ee062..4dec44a 100644
--- a/tests/unit/sdk/test_node.py
+++ b/tests/unit/sdk/test_node.py
@@ -197,15 +197,15 @@ async def test_init_node_data_graphql(client, location_schema: NodeSchemaAPI, lo
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_no_filters(clients, location_schema: NodeSchemaAPI, client_type):
+async def test_query_data_no_filters_property(clients, location_schema: NodeSchemaAPI, client_type):
     if client_type == "standard":
         client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         node = InfrahubNode(client=client, schema=location_schema)
-        data = await node.generate_query_data()
+        data = await node.generate_query_data(property=True)
     else:
         client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         node = InfrahubNodeSync(client=client, schema=location_schema)
-        data = node.generate_query_data()
+        data = node.generate_query_data(property=True)
 
     assert data == {
         "BuiltinLocation": {
@@ -297,15 +297,59 @@ async def test_query_data_no_filters(clients, location_schema: NodeSchemaAPI, cl
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_node(clients, location_schema: NodeSchemaAPI, client_type):
+async def test_query_data_no_filters(clients, location_schema: NodeSchemaAPI, client_type):
     if client_type == "standard":
         client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         node = InfrahubNode(client=client, schema=location_schema)
-        data = await node.generate_query_data_node()
+        data = await node.generate_query_data()
     else:
         client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         node = InfrahubNodeSync(client=client, schema=location_schema)
-        data = node.generate_query_data_node()
+        data = node.generate_query_data()
+
+    assert data == {
+        "BuiltinLocation": {
+            "@filters": {},
+            "count": None,
+            "edges": {
+                "node": {
+                    "__typename": None,
+                    "id": None,
+                    "hfid": None,
+                    "display_label": None,
+                    "name": {
+                        "value": None,
+                    },
+                    "description": {
+                        "value": None,
+                    },
+                    "type": {
+                        "value": None,
+                    },
+                    "primary_tag": {
+                        "node": {
+                            "id": None,
+                            "hfid": None,
+                            "display_label": None,
+                            "__typename": None,
+                        },
+                    },
+                },
+            },
+        },
+    }
+
+
+@pytest.mark.parametrize("client_type", client_types)
+async def test_query_data_node_property(clients, location_schema: NodeSchemaAPI, client_type):
+    if client_type == "standard":
+        client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        node = InfrahubNode(client=client, schema=location_schema)
+        data = await node.generate_query_data_node(property=True)
+    else:
+        client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        node = InfrahubNodeSync(client=client, schema=location_schema)
+        data = node.generate_query_data_node(property=True)
 
     assert data == {
         "name": {
@@ -361,17 +405,49 @@ async def test_query_data_node(clients, location_schema: NodeSchemaAPI, client_t
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_with_prefetch_relationships(clients, mock_schema_query_02, client_type):
+async def test_query_data_node(clients, location_schema: NodeSchemaAPI, client_type):
+    if client_type == "standard":
+        client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        node = InfrahubNode(client=client, schema=location_schema)
+        data = await node.generate_query_data_node()
+    else:
+        client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        node = InfrahubNodeSync(client=client, schema=location_schema)
+        data = node.generate_query_data_node()
+
+    assert data == {
+        "name": {
+            "value": None,
+        },
+        "description": {
+            "value": None,
+        },
+        "type": {
+            "value": None,
+        },
+        "primary_tag": {
+            "node": {
+                "id": None,
+                "hfid": None,
+                "display_label": None,
+                "__typename": None,
+            },
+        },
+    }
+
+
+@pytest.mark.parametrize("client_type", client_types)
+async def test_query_data_with_prefetch_relationships_property(clients, mock_schema_query_02, client_type):
     if client_type == "standard":
         client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         location_schema: GenericSchema = await client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
         node = InfrahubNode(client=client, schema=location_schema)
-        data = await node.generate_query_data(prefetch_relationships=True)
+        data = await node.generate_query_data(prefetch_relationships=True, property=True)
     else:
         client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         location_schema: GenericSchema = client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
         node = InfrahubNodeSync(client=client, schema=location_schema)
-        data = node.generate_query_data(prefetch_relationships=True)
+        data = node.generate_query_data(prefetch_relationships=True, property=True)
 
     assert data == {
         "BuiltinLocation": {
@@ -497,17 +573,69 @@ async def test_query_data_with_prefetch_relationships(clients, mock_schema_query
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_node_with_prefetch_relationships(clients, mock_schema_query_02, client_type):
+async def test_query_data_with_prefetch_relationships(clients, mock_schema_query_02, client_type):
     if client_type == "standard":
         client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         location_schema: GenericSchema = await client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
         node = InfrahubNode(client=client, schema=location_schema)
-        data = await node.generate_query_data_node(prefetch_relationships=True)
+        data = await node.generate_query_data(prefetch_relationships=True)
     else:
         client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         location_schema: GenericSchema = client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
         node = InfrahubNodeSync(client=client, schema=location_schema)
-        data = node.generate_query_data_node(prefetch_relationships=True)
+        data = node.generate_query_data(prefetch_relationships=True)
+
+    assert data == {
+        "BuiltinLocation": {
+            "@filters": {},
+            "count": None,
+            "edges": {
+                "node": {
+                    "__typename": None,
+                    "id": None,
+                    "hfid": None,
+                    "display_label": None,
+                    "name": {
+                        "value": None,
+                    },
+                    "description": {
+                        "value": None,
+                    },
+                    "type": {
+                        "value": None,
+                    },
+                    "primary_tag": {
+                        "node": {
+                            "id": None,
+                            "hfid": None,
+                            "display_label": None,
+                            "__typename": None,
+                            "description": {
+                                "value": None,
+                            },
+                            "name": {
+                                "value": None,
+                            },
+                        },
+                    },
+                },
+            },
+        },
+    }
+
+
+@pytest.mark.parametrize("client_type", client_types)
+async def test_query_data_node_with_prefetch_relationships_property(clients, mock_schema_query_02, client_type):
+    if client_type == "standard":
+        client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        location_schema: GenericSchema = await client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
+        node = InfrahubNode(client=client, schema=location_schema)
+        data = await node.generate_query_data_node(prefetch_relationships=True, property=True)
+    else:
+        client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        location_schema: GenericSchema = client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
+        node = InfrahubNodeSync(client=client, schema=location_schema)
+        data = node.generate_query_data_node(prefetch_relationships=True, property=True)
 
     assert data == {
         "description": {
@@ -573,17 +701,57 @@ async def test_query_data_node_with_prefetch_relationships(clients, mock_schema_
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_generic(clients, mock_schema_query_02, client_type):  # pylint: disable=unused-argument
+async def test_query_data_node_with_prefetch_relationships(clients, mock_schema_query_02, client_type):
+    if client_type == "standard":
+        client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        location_schema: GenericSchema = await client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
+        node = InfrahubNode(client=client, schema=location_schema)
+        data = await node.generate_query_data_node(prefetch_relationships=True)
+    else:
+        client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        location_schema: GenericSchema = client.schema.get(kind="BuiltinLocation")  # type: ignore[annotation-unchecked]
+        node = InfrahubNodeSync(client=client, schema=location_schema)
+        data = node.generate_query_data_node(prefetch_relationships=True)
+
+    assert data == {
+        "description": {
+            "value": None,
+        },
+        "name": {
+            "value": None,
+        },
+        "primary_tag": {
+            "node": {
+                "__typename": None,
+                "description": {
+                    "value": None,
+                },
+                "display_label": None,
+                "id": None,
+                "hfid": None,
+                "name": {
+                    "value": None,
+                },
+            },
+        },
+        "type": {
+            "value": None,
+        },
+    }
+
+
+@pytest.mark.parametrize("client_type", client_types)
+async def test_query_data_generic_property(clients, mock_schema_query_02, client_type):  # pylint: disable=unused-argument
     if client_type == "standard":
         client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         corenode_schema: GenericSchema = await client.schema.get(kind="CoreNode")  # type: ignore[annotation-unchecked]
         node = InfrahubNode(client=client, schema=corenode_schema)
-        data = await node.generate_query_data(fragment=False)
+        data = await node.generate_query_data(fragment=False, property=True)
     else:
         client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         corenode_schema: GenericSchema = client.schema.get(kind="CoreNode")  # type: ignore[annotation-unchecked]
         node = InfrahubNodeSync(client=client, schema=corenode_schema)
-        data = node.generate_query_data(fragment=False)
+        data = node.generate_query_data(fragment=False, property=True)
 
     assert data == {
         "CoreNode": {
@@ -602,17 +770,17 @@ async def test_query_data_generic(clients, mock_schema_query_02, client_type):
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_generic_fragment(clients, mock_schema_query_02, client_type):  # pylint: disable=unused-argument
+async def test_query_data_generic_fragment_property(clients, mock_schema_query_02, client_type):  # pylint: disable=unused-argument
     if client_type == "standard":
         client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         corenode_schema: GenericSchema = await client.schema.get(kind="CoreNode")  # type: ignore[annotation-unchecked]
         node = InfrahubNode(client=client, schema=corenode_schema)
-        data = await node.generate_query_data(fragment=True)
+        data = await node.generate_query_data(fragment=True, property=True)
     else:
         client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
         corenode_schema: GenericSchema = client.schema.get(kind="CoreNode")  # type: ignore[annotation-unchecked]
         node = InfrahubNodeSync(client=client, schema=corenode_schema)
-        data = node.generate_query_data(fragment=True)
+        data = node.generate_query_data(fragment=True, property=True)
 
     assert data == {
         "CoreNode": {
@@ -748,13 +916,75 @@ async def test_query_data_generic_fragment(clients, mock_schema_query_02, client
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_include(client, location_schema: NodeSchemaAPI, client_type):
+async def test_query_data_generic_fragment(clients, mock_schema_query_02, client_type):  # pylint: disable=unused-argument
+    if client_type == "standard":
+        client: InfrahubClient = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        corenode_schema: GenericSchema = await client.schema.get(kind="CoreNode")  # type: ignore[annotation-unchecked]
+        node = InfrahubNode(client=client, schema=corenode_schema)
+        data = await node.generate_query_data(fragment=True)
+    else:
+        client: InfrahubClientSync = getattr(clients, client_type)  # type: ignore[annotation-unchecked]
+        corenode_schema: GenericSchema = client.schema.get(kind="CoreNode")  # type: ignore[annotation-unchecked]
+        node = InfrahubNodeSync(client=client, schema=corenode_schema)
+        data = node.generate_query_data(fragment=True)
+
+    assert data == {
+        "CoreNode": {
+            "@filters": {},
+            "count": None,
+            "edges": {
+                "node": {
+                    "__typename": None,
+                    "...on BuiltinLocation": {
+                        "description": {
+                            "@alias": "__alias__BuiltinLocation__description",
+                            "value": None,
+                        },
+                        "name": {
+                            "@alias": "__alias__BuiltinLocation__name",
+                            "value": None,
+                        },
+                        "primary_tag": {
+                            "@alias": "__alias__BuiltinLocation__primary_tag",
+                            "node": {
+                                "__typename": None,
+                                "display_label": None,
+                                "id": None,
+                                "hfid": None,
+                            },
+                        },
+                        "type": {
+                            "@alias": "__alias__BuiltinLocation__type",
+                            "value": None,
+                        },
+                    },
+                    "...on BuiltinTag": {
+                        "description": {
+                            "@alias": "__alias__BuiltinTag__description",
+                            "value": None,
+                        },
+                        "name": {
+                            "@alias": "__alias__BuiltinTag__name",
+                            "value": None,
+                        },
+                    },
+                    "display_label": None,
+                    "id": None,
+                    "hfid": None,
+                },
+            },
+        },
+    }
+
+
+@pytest.mark.parametrize("client_type", client_types)
+async def test_query_data_include_property(client, location_schema: NodeSchemaAPI, client_type):
     if client_type == "standard":
         node = InfrahubNode(client=client, schema=location_schema)
-        data = await node.generate_query_data(include=["tags"])
+        data = await node.generate_query_data(include=["tags"], property=True)
     else:
         node = InfrahubNodeSync(client=client, schema=location_schema)
-        data = node.generate_query_data(include=["tags"])
+        data = node.generate_query_data(include=["tags"], property=True)
 
     assert data == {
         "BuiltinLocation": {
@@ -870,13 +1100,65 @@ async def test_query_data_include(client, location_schema: NodeSchemaAPI, client
 
 
 @pytest.mark.parametrize("client_type", client_types)
-async def test_query_data_exclude(client, location_schema: NodeSchemaAPI, client_type):
+async def test_query_data_include(client, location_schema: NodeSchemaAPI, client_type):
     if client_type == "standard":
         node = InfrahubNode(client=client, schema=location_schema)
-        data = await node.generate_query_data(exclude=["description", "primary_tag"])
+        data = await node.generate_query_data(include=["tags"])
     else:
         node = InfrahubNodeSync(client=client, schema=location_schema)
-        data = node.generate_query_data(exclude=["description", "primary_tag"])
+        data = node.generate_query_data(include=["tags"])
+
+    assert data == {
+        "BuiltinLocation": {
+            "@filters": {},
+            "count": None,
+            "edges": {
+                "node": {
+                    "__typename": None,
+                    "id": None,
+                    "hfid": None,
+                    "display_label": None,
+                    "name": {
+                        "value": None,
+                    },
+                    "description": {
+                        "value": None,
+                    },
+                    "type": {
+                        "value": None,
+                    },
+                    "primary_tag": {
+                        "node": {
+                            "id": None,
+                            "hfid": None,
+                            "display_label": None,
+                            "__typename": None,
+                        },
+                    },
+                    "tags": {
+                        "count": None,
+                        "edges": {
+                            "node": {
+                                "id": None,
+                                "display_label": None,
+                                "__typename": None,
+                            },
+                        },
+                    },
+                },
+            },
+        },
+    }
+
+
+@pytest.mark.parametrize("client_type", client_types)
+async def test_query_data_exclude_property(client, location_schema: NodeSchemaAPI, client_type):
+    if client_type == "standard":
+        node = InfrahubNode(client=client, schema=location_schema)
+        data = await node.generate_query_data(exclude=["description", "primary_tag"], property=True)
+    else:
+        node = InfrahubNodeSync(client=client, schema=location_schema)
+        data = node.generate_query_data(exclude=["description", "primary_tag"], property=True)
 
     assert data == {
         "BuiltinLocation": {
@@ -928,6 +1210,37 @@ async def test_query_data_exclude(client, location_schema: NodeSchemaAPI, client
     }
 
 
+@pytest.mark.parametrize("client_type", client_types)
+async def test_query_data_exclude(client, location_schema: NodeSchemaAPI, client_type):
+    if client_type == "standard":
+        node = InfrahubNode(client=client, schema=location_schema)
+        data = await node.generate_query_data(exclude=["description", "primary_tag"])
+    else:
+        node = InfrahubNodeSync(client=client, schema=location_schema)
+        data = node.generate_query_data(exclude=["description", "primary_tag"])
+
+    assert data == {
+        "BuiltinLocation": {
+            "@filters": {},
+            "count": None,
+            "edges": {
+                "node": {
+                    "__typename": None,
+                    "id": None,
+                    "hfid": None,
+                    "display_label": None,
+                    "name": {
+                        "value": None,
+                    },
+                    "type": {
+                        "value": None,
+                    },
+                },
+            },
+        },
+    }
+
+
 @pytest.mark.parametrize("client_type", client_types)
 async def test_create_input_data(client, location_schema: NodeSchemaAPI, client_type):
     data = {"name": {"value": "JFK1"}, "description": {"value": "JFK Airport"}, "type": {"value": "SITE"}}
@@ -1112,7 +1425,9 @@ async def test_create_input_data_with_relationships_03_for_update_include_unmodi
 
 @pytest.mark.parametrize("client_type", client_types)
 async def test_create_input_data_with_relationships_03_for_update_exclude_unmodified(
-    clients, rfile_schema, client_type
+    clients,
+    rfile_schema,
+    client_type,
 ):
     data = {
         "name": {"value": "rfile01", "is_protected": True, "source": "ffffffff"},