diff --git a/swh/graphql/app.py b/swh/graphql/app.py
--- a/swh/graphql/app.py
+++ b/swh/graphql/app.py
@@ -26,15 +26,19 @@
     resolvers.visit_status,
     resolvers.snapshot,
     resolvers.snapshot_branch,
+    resolvers.branch_target,
+    resolvers.branch_target_node,
     resolvers.revision,
     resolvers.release,
+    resolvers.release_target,
+    resolvers.release_target_node,
     resolvers.directory,
     resolvers.directory_entry,
-    resolvers.search_result,
-    resolvers.branch_target,
-    resolvers.release_target,
     resolvers.directory_entry_target,
+    resolvers.directory_entry_target_node,
+    resolvers.search_result,
     resolvers.search_result_target,
+    resolvers.search_result_target_node,
     resolvers.binary_string,
     resolvers.date,
     scalars.id_scalar,
diff --git a/swh/graphql/resolvers/content.py b/swh/graphql/resolvers/content.py
--- a/swh/graphql/resolvers/content.py
+++ b/swh/graphql/resolvers/content.py
@@ -3,7 +3,7 @@
 # License: GNU General Public License version 3, or any later version
 # See top-level LICENSE file for more information
 
-from typing import Dict, List, Optional, Union
+from typing import Dict, List, Optional
 
 from swh.graphql.errors import DataError, InvalidInputError
 from swh.model import hashutil
@@ -11,10 +11,7 @@
 
 from .base_connection import BaseList
 from .base_node import BaseSWHNode
-from .directory_entry import BaseDirectoryEntryNode
-from .release import BaseReleaseNode
-from .search import SearchResultNode
-from .snapshot_branch import BaseSnapshotBranchNode
+from .target import TargetNode
 
 
 def read_and_validate_content_hashes(hashes) -> Dict[str, bytes]:
@@ -94,17 +91,12 @@
     """
 
     _can_be_null = True
-    obj: Union[
-        SearchResultNode,
-        BaseDirectoryEntryNode,
-        BaseReleaseNode,
-        BaseSnapshotBranchNode,
-    ]
+    obj: TargetNode
 
     def _get_node_data(self) -> Optional[Content]:
         # FIXME, this is not considering hash collisions
         # and could return a wrong object in very rare situations
-        contents = self.archive.get_contents(hashes={"sha1_git": self.obj.target_hash})
+        contents = self.archive.get_contents(hashes={"sha1_git": self.obj.target_id})
         # always returning the first content from the storage
         return contents[0] if contents else None
 
diff --git a/swh/graphql/resolvers/directory.py b/swh/graphql/resolvers/directory.py
--- a/swh/graphql/resolvers/directory.py
+++ b/swh/graphql/resolvers/directory.py
@@ -3,16 +3,12 @@
 # License: GNU General Public License version 3, or any later version
 # See top-level LICENSE file for more information
 
-from typing import Union
-
 from swh.model.model import Directory
 from swh.model.swhids import ObjectType
 
 from .base_node import BaseSWHNode
-from .release import BaseReleaseNode
 from .revision import BaseRevisionNode
-from .search import SearchResultNode
-from .snapshot_branch import BaseSnapshotBranchNode
+from .target import TargetNode
 
 
 class BaseDirectoryNode(BaseSWHNode):
@@ -65,15 +61,8 @@
     Node resolver for a directory requested as a target
     """
 
-    from .directory_entry import BaseDirectoryEntryNode
-
     _can_be_null = True
-    obj: Union[
-        BaseSnapshotBranchNode,
-        BaseReleaseNode,
-        BaseDirectoryEntryNode,
-        SearchResultNode,
-    ]
+    obj: TargetNode
 
     def _get_node_data(self):
-        return self._get_directory_by_id(self.obj.target_hash)
+        return self._get_directory_by_id(self.obj.target_id)
diff --git a/swh/graphql/resolvers/directory_entry.py b/swh/graphql/resolvers/directory_entry.py
--- a/swh/graphql/resolvers/directory_entry.py
+++ b/swh/graphql/resolvers/directory_entry.py
@@ -12,11 +12,11 @@
 
 class BaseDirectoryEntryNode(BaseNode):
     @property
-    def target_hash(self):  # for DirectoryNode
+    def target_id(self):
         return self._node.target
 
     @property
-    def targetType(self):  # To support the schema naming convention
+    def target_type(self):
         mapping = {"file": "content", "dir": "directory", "rev": "revision"}
         return mapping.get(self._node.type)
 
diff --git a/swh/graphql/resolvers/origin.py b/swh/graphql/resolvers/origin.py
--- a/swh/graphql/resolvers/origin.py
+++ b/swh/graphql/resolvers/origin.py
@@ -8,7 +8,7 @@
 
 from .base_connection import BaseConnection
 from .base_node import BaseSWHNode
-from .search import SearchResultNode
+from .target import TargetNode
 
 
 class BaseOriginNode(BaseSWHNode):
@@ -32,13 +32,13 @@
     Node resolver for an origin requested as a target
     """
 
-    obj: SearchResultNode
+    obj: TargetNode
 
     def _get_node_data(self):
-        # The target origin URL is guaranteed to exist in the archive
+        # The target origin URL (target_id here) is guaranteed to exist in the archive
         # Hence returning the origin object without any explicit check in the archive
         # This assumes that the search index and archive are in sync
-        return Origin(self.obj.target_url)
+        return Origin(self.obj.target_id)
 
 
 class OriginConnection(BaseConnection):
diff --git a/swh/graphql/resolvers/release.py b/swh/graphql/resolvers/release.py
--- a/swh/graphql/resolvers/release.py
+++ b/swh/graphql/resolvers/release.py
@@ -3,11 +3,8 @@
 # License: GNU General Public License version 3, or any later version
 # See top-level LICENSE file for more information
 
-from typing import Union
-
 from .base_node import BaseSWHNode
-from .search import SearchResultNode
-from .snapshot_branch import BaseSnapshotBranchNode
+from .target import TargetNode
 
 
 class BaseReleaseNode(BaseSWHNode):
@@ -19,11 +16,11 @@
         return self.archive.get_releases([release_id])[0]
 
     @property
-    def target_hash(self):
+    def target_id(self):
         return self._node.target
 
     @property
-    def targetType(self):  # To support the schema naming convention
+    def target_type(self):
         return self._node.target_type.value
 
     def is_type_of(self):
@@ -47,8 +44,7 @@
     """
 
     _can_be_null = True
-    obj: Union[BaseSnapshotBranchNode, BaseReleaseNode, SearchResultNode]
+    obj: TargetNode
 
     def _get_node_data(self):
-        # self.obj.target_hash is the requested release id
-        return self._get_release_by_id(self.obj.target_hash)
+        return self._get_release_by_id(self.obj.target_id)
diff --git a/swh/graphql/resolvers/resolver_factory.py b/swh/graphql/resolvers/resolver_factory.py
--- a/swh/graphql/resolvers/resolver_factory.py
+++ b/swh/graphql/resolvers/resolver_factory.py
@@ -33,7 +33,8 @@
     TargetSnapshotNode,
     VisitSnapshotNode,
 )
-from .snapshot_branch import AliasSnapshotBranchNode, SnapshotBranchConnection
+from .snapshot_branch import SnapshotBranchConnection, TargetSnapshotBranchNode
+from .target import TargetNode
 from .visit import LatestVisitNode, OriginVisitConnection, OriginVisitNode
 from .visit_status import LatestVisitStatusNode, VisitStatusConnection
 
@@ -46,31 +47,20 @@
         "latest-status": LatestVisitStatusNode,
         "visit-snapshot": VisitSnapshotNode,
         "snapshot": SnapshotNode,
-        "branch-alias": AliasSnapshotBranchNode,
-        "branch-revision": TargetRevisionNode,
-        "branch-release": TargetReleaseNode,
-        "branch-directory": TargetDirectoryNode,
-        "branch-content": TargetContentNode,
-        "branch-snapshot": TargetSnapshotNode,
         "revision": RevisionNode,
         "revision-directory": RevisionDirectoryNode,
         "release": ReleaseNode,
-        "release-revision": TargetRevisionNode,
-        "release-release": TargetReleaseNode,
-        "release-directory": TargetDirectoryNode,
-        "release-content": TargetContentNode,
         "directory": DirectoryNode,
         "directory-entry": DirectoryEntryNode,
         "content-by-hashes": ContentbyHashesNode,
-        "dir-entry-content": TargetContentNode,
-        "dir-entry-directory": TargetDirectoryNode,
-        "dir-entry-revision": TargetRevisionNode,
-        "search-result-origin": TargetOriginNode,
-        "search-result-snapshot": TargetSnapshotNode,
-        "search-result-revision": TargetRevisionNode,
-        "search-result-release": TargetReleaseNode,
-        "search-result-directory": TargetDirectoryNode,
-        "search-result-content": TargetContentNode,
+        "target": TargetNode,
+        "target-alias": TargetSnapshotBranchNode,
+        "target-origin": TargetOriginNode,
+        "target-revision": TargetRevisionNode,
+        "target-release": TargetReleaseNode,
+        "target-snapshot": TargetSnapshotNode,
+        "target-directory": TargetDirectoryNode,
+        "target-content": TargetContentNode,
     }
 
     @classmethod
diff --git a/swh/graphql/resolvers/resolvers.py b/swh/graphql/resolvers/resolvers.py
--- a/swh/graphql/resolvers/resolvers.py
+++ b/swh/graphql/resolvers/resolvers.py
@@ -38,18 +38,22 @@
 visit_status: ObjectType = ObjectType("VisitStatus")
 snapshot: ObjectType = ObjectType("Snapshot")
 snapshot_branch: ObjectType = ObjectType("Branch")
+branch_target: ObjectType = ObjectType("BranchTarget")
 revision: ObjectType = ObjectType("Revision")
 release: ObjectType = ObjectType("Release")
+release_target: ObjectType = ObjectType("ReleaseTarget")
 directory: ObjectType = ObjectType("Directory")
 directory_entry: ObjectType = ObjectType("DirectoryEntry")
+directory_entry_target: ObjectType = ObjectType("DirectoryEntryTarget")
 search_result: ObjectType = ObjectType("SearchResult")
+search_result_target: ObjectType = ObjectType("SearchResultTarget")
 binary_string: ObjectType = ObjectType("BinaryString")
 date: ObjectType = ObjectType("Date")
 
-branch_target: UnionType = UnionType("BranchTarget")
-release_target: UnionType = UnionType("ReleaseTarget")
-directory_entry_target: UnionType = UnionType("DirectoryEntryTarget")
-search_result_target: UnionType = UnionType("SearchResultTarget")
+branch_target_node: UnionType = UnionType("BranchTargetNode")
+release_target_node: UnionType = UnionType("ReleaseTargetNode")
+directory_entry_target_node: UnionType = UnionType("DirectoryEntryTargetNode")
+search_result_target_node: UnionType = UnionType("SearchResultTargetNode")
 
 # Node resolvers
 # A node resolver will return either an instance of a BaseNode subclass or None
@@ -95,24 +99,6 @@
     return NodeObjectFactory.create("visit-snapshot", obj, info, **kw)
 
 
-@snapshot_branch.field("target")
-def snapshot_branch_target_resolver(
-    obj: rs.snapshot_branch.BaseSnapshotBranchNode, info: GraphQLResolveInfo, **kw
-) -> Union[
-    rs.revision.BaseRevisionNode,
-    rs.release.BaseReleaseNode,
-    rs.directory.BaseDirectoryNode,
-    rs.content.BaseContentNode,
-    rs.snapshot.BaseSnapshotNode,
-    rs.snapshot_branch.BaseSnapshotBranchNode,
-]:
-    """
-    Snapshot branch target can be a revision, release, directory,
-    content, snapshot or a branch itself (alias type)
-    """
-    return NodeObjectFactory.create(f"branch-{obj.targetType}", obj, info, **kw)
-
-
 @query.field("revision")
 def revision_resolver(
     obj: None, info: GraphQLResolveInfo, **kw
@@ -134,21 +120,6 @@
     return NodeObjectFactory.create("release", obj, info, **kw)
 
 
-@release.field("target")
-def release_target_resolver(
-    obj: rs.release.BaseReleaseNode, info: GraphQLResolveInfo, **kw
-) -> Union[
-    rs.revision.BaseRevisionNode,
-    rs.release.BaseReleaseNode,
-    rs.directory.BaseDirectoryNode,
-    rs.content.BaseContentNode,
-]:
-    """
-    Release target can be a release, revision, directory or a content
-    """
-    return NodeObjectFactory.create(f"release-{obj.targetType}", obj, info, **kw)
-
-
 @query.field("directory")
 def directory_resolver(
     obj: None, info: GraphQLResolveInfo, **kw
@@ -163,43 +134,38 @@
     return NodeObjectFactory.create("directory-entry", obj, info, **kw)
 
 
-@directory_entry.field("target")
-def directory_entry_target_resolver(
-    obj: rs.directory_entry.BaseDirectoryEntryNode, info: GraphQLResolveInfo, **kw
-) -> Union[
-    rs.revision.BaseRevisionNode,
-    rs.directory.BaseDirectoryNode,
-    rs.content.BaseContentNode,
-]:
-    """
-    DirectoryEntry target can be a directory, content or a revision
-    """
-    return NodeObjectFactory.create(f"dir-entry-{obj.targetType}", obj, info, **kw)
+@query.field("contentByHashes")
+def content_by_hashes_resolver(
+    obj: None, info: GraphQLResolveInfo, **kw
+) -> rs.content.ContentbyHashesNode:
+    return NodeObjectFactory.create("content-by-hashes", obj, info, **kw)
 
 
+@snapshot_branch.field("target")
 @search_result.field("target")
-def search_result_target_resolver(
-    obj: rs.search.SearchResultNode, info: GraphQLResolveInfo, **kw
+@directory_entry.field("target")
+@release.field("target")
+def generic_target_resolver(
+    obj: Union[rs.release.BaseReleaseNode, rs.directory_entry.BaseDirectoryEntryNode],
+    info: GraphQLResolveInfo,
+    **kw,
+) -> rs.target.TargetNode:
+    return NodeObjectFactory.create("target", obj, info, **kw)
+
+
+@branch_target.field("node")
+@directory_entry_target.field("node")
+@search_result_target.field("node")
+@release_target.field("node")
+def target_node_resolver(
+    obj: rs.target.TargetNode, info: GraphQLResolveInfo, **kw
 ) -> Union[
-    rs.origin.BaseOriginNode,
-    rs.snapshot.BaseSnapshotNode,
     rs.revision.BaseRevisionNode,
     rs.release.BaseReleaseNode,
     rs.directory.BaseDirectoryNode,
     rs.content.BaseContentNode,
 ]:
-    """
-    SearchResult target can be an origin, snapshot, revision, release
-    directory or a content
-    """
-    return NodeObjectFactory.create(f"search-result-{obj.targetType}", obj, info, **kw)
-
-
-@query.field("contentByHashes")
-def content_by_hashes_resolver(
-    obj: None, info: GraphQLResolveInfo, **kw
-) -> rs.content.ContentbyHashesNode:
-    return NodeObjectFactory.create("content-by-hashes", obj, info, **kw)
+    return NodeObjectFactory.create(f"target-{obj.type}", obj, info, **kw)
 
 
 # Connection resolvers
@@ -317,10 +283,10 @@
 # Other resolvers
 
 
-@release_target.type_resolver
-@directory_entry_target.type_resolver
-@branch_target.type_resolver
-@search_result_target.type_resolver
+@release_target_node.type_resolver
+@directory_entry_target_node.type_resolver
+@search_result_target_node.type_resolver
+@branch_target_node.type_resolver
 def union_resolver(
     obj: Union[
         rs.origin.BaseOriginNode,
diff --git a/swh/graphql/resolvers/revision.py b/swh/graphql/resolvers/revision.py
--- a/swh/graphql/resolvers/revision.py
+++ b/swh/graphql/resolvers/revision.py
@@ -3,7 +3,7 @@
 # License: GNU General Public License version 3, or any later version
 # See top-level LICENSE file for more information
 
-from typing import Union
+# from typing import Union
 
 from swh.graphql.utils import utils
 from swh.model.model import Revision
@@ -12,10 +12,7 @@
 
 from .base_connection import BaseConnection
 from .base_node import BaseSWHNode
-from .directory_entry import BaseDirectoryEntryNode
-from .release import BaseReleaseNode
-from .search import SearchResultNode
-from .snapshot_branch import BaseSnapshotBranchNode
+from .target import TargetNode
 
 
 class BaseRevisionNode(BaseSWHNode):
@@ -66,16 +63,10 @@
     """
 
     _can_be_null = True
-    obj: Union[
-        BaseSnapshotBranchNode,
-        BaseReleaseNode,
-        BaseDirectoryEntryNode,
-        SearchResultNode,
-    ]
+    obj: TargetNode
 
     def _get_node_data(self):
-        # self.obj.target_hash is the requested revision id
-        return self._get_revision_by_id(self.obj.target_hash)
+        return self._get_revision_by_id(self.obj.target_id)
 
 
 class ParentRevisionConnection(BaseConnection):
diff --git a/swh/graphql/resolvers/scalars.py b/swh/graphql/resolvers/scalars.py
--- a/swh/graphql/resolvers/scalars.py
+++ b/swh/graphql/resolvers/scalars.py
@@ -4,7 +4,7 @@
 # See top-level LICENSE file for more information
 
 import datetime
-from typing import Optional
+from typing import Optional, Union
 
 from ariadne import ScalarType
 
@@ -19,10 +19,10 @@
 
 
 @id_scalar.serializer
-def serialize_id(value) -> str:
-    if type(value) is str:
-        value = value.encode()
-    return value.hex()
+def serialize_id(value: Union[bytes, str]) -> str:
+    if isinstance(value, bytes):
+        return value.hex()
+    return value
 
 
 @datetime_scalar.serializer
diff --git a/swh/graphql/resolvers/search.py b/swh/graphql/resolvers/search.py
--- a/swh/graphql/resolvers/search.py
+++ b/swh/graphql/resolvers/search.py
@@ -13,9 +13,13 @@
     """ """
 
     @property
-    def targetType(self):  # To support the schema naming convention
+    def target_type(self):
         return self._node.type
 
+    @property
+    def target_id(self):
+        return self._node.target_id
+
 
 class ResolveSwhidList(BaseList):
 
@@ -28,7 +32,7 @@
         if self.archive.is_object_available(swhid.object_id, swhid.object_type):
             results = [
                 {
-                    "target_hash": swhid.object_id,
+                    "target_id": swhid.object_id,
                     "type": swhid.object_type.name.lower(),
                 }
             ]
@@ -48,6 +52,6 @@
 
         # FIXME hard coding type to origin for now, as it is the only searchable object
         results = [
-            {"target_url": ori["url"], "type": "origin"} for ori in origins.results
+            {"target_id": ori["url"], "type": "origin"} for ori in origins.results
         ]
         return PagedResult(results=results, next_page_token=origins.next_page_token)
diff --git a/swh/graphql/resolvers/snapshot.py b/swh/graphql/resolvers/snapshot.py
--- a/swh/graphql/resolvers/snapshot.py
+++ b/swh/graphql/resolvers/snapshot.py
@@ -77,7 +77,7 @@
     obj: Union[SearchResultNode, BaseSnapshotBranchNode]
 
     def _get_node_data(self):
-        snapshot_id = self.obj.target_hash
+        snapshot_id = self.obj.target_id
         return self._get_snapshot_by_id(snapshot_id)
 
 
diff --git a/swh/graphql/resolvers/snapshot_branch.py b/swh/graphql/resolvers/snapshot_branch.py
--- a/swh/graphql/resolvers/snapshot_branch.py
+++ b/swh/graphql/resolvers/snapshot_branch.py
@@ -11,6 +11,7 @@
 
 from .base_connection import BaseConnection
 from .base_node import BaseNode
+from .target import TargetNode
 
 
 class BaseSnapshotBranchNode(BaseNode):
@@ -25,18 +26,14 @@
         branch_name, branch_obj = node_data
         node = {
             "name": branch_name,
-            "type": branch_obj.target_type.value,
-            "target_hash": branch_obj.target,
+            "target_type": branch_obj.target_type.value,
+            "target_id": branch_obj.target,
         }
         return namedtuple("NodeObj", node.keys())(*node.values())
 
     def is_type_of(self):
         return "Branch"
 
-    @property
-    def targetType(self):  # To support the schema naming convention
-        return self._node.type
-
     def snapshot_swhid(self):
         """
         Logic to handle multiple branch alias redirects
@@ -59,13 +56,13 @@
         raise ObjectNotFoundError("There is no snapshot associated with the branch")
 
 
-class AliasSnapshotBranchNode(BaseSnapshotBranchNode):
+class TargetSnapshotBranchNode(BaseSnapshotBranchNode):
 
-    obj: BaseSnapshotBranchNode
+    obj: TargetNode
 
     def _get_node_data(self):
         snapshot_swhid = self.snapshot_swhid()
-        target_branch = self.obj.target_hash
+        target_branch = self.obj.target_id
 
         alias_branch = self.archive.get_snapshot_branches(
             snapshot_swhid.object_id, first=1, name_include=target_branch
diff --git a/swh/graphql/resolvers/target.py b/swh/graphql/resolvers/target.py
new file mode 100644
--- /dev/null
+++ b/swh/graphql/resolvers/target.py
@@ -0,0 +1,40 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Union
+
+from .base_node import BaseNode
+
+if TYPE_CHECKING:
+    from .directory_entry import BaseDirectoryEntryNode  # pragma: no cover
+    from .release import BaseReleaseNode  # pragma: no cover
+    from .search import SearchResultNode  # pragma: no cover
+    from .snapshot_branch import BaseSnapshotBranchNode  # pragma: no cover
+
+
+class TargetNode(BaseNode):
+    """
+    Generic resolver for all the targets
+    Every parent object (self.obj) must have two attributes
+    target_type and target_id
+
+    Create a Subclass for specific target nodes (say one with extra fields)
+    """
+
+    obj: Union[
+        BaseReleaseNode,
+        BaseDirectoryEntryNode,
+        SearchResultNode,
+        BaseSnapshotBranchNode,
+    ]
+
+    def _get_node_data(self):
+        # node field of a target object is resolved in the top level resolver
+        return {
+            # field exposed in the schema
+            "type": self.obj.target_type,
+            # field exposed in the schema, the same value is used to
+            # retrieve the node object
+            "identifier": self.obj.target_id,
+            # field NOT exposed in the schema, used to retrieve the target node
+            "target_id": self.obj.target_id,
+        }
diff --git a/swh/graphql/schema/schema.graphql b/swh/graphql/schema/schema.graphql
--- a/swh/graphql/schema/schema.graphql
+++ b/swh/graphql/schema/schema.graphql
@@ -493,10 +493,21 @@
   fullname: BinaryString
 }
 
+
 """
-Possible branch target objects
+A snapshot branch object
 """
-union BranchTarget = Revision | Release | Branch | Content | Directory | Snapshot
+type Branch {
+  """
+  Branch name
+  """
+  name: BinaryString
+
+  """
+  Branch target object
+  """
+  target: BranchTarget
+}
 
 """
 Possible Branch target types
@@ -511,23 +522,28 @@
 }
 
 """
-A snapshot branch object
+Possible branch target nodes
 """
-type Branch {
+union BranchTargetNode = Revision | Release | Branch | Content | Directory | Snapshot
+
+"""
+A branch target object
+"""
+type BranchTarget {
   """
-  Branch name
+  Branch target type
   """
-  name: BinaryString
+  type: BranchTargetType!
 
   """
-  Type of Branch target
+  Branch target identifier
   """
-  targetType: BranchTargetType
+  identifier: ID!
 
   """
-  Branch target object
+  Branch target node
   """
-  target: BranchTarget
+  node: BranchTargetNode
 }
 
 """
@@ -666,21 +682,6 @@
   ): RevisionConnection
 }
 
-"""
-Possible release target objects
-"""
-union ReleaseTarget = Release | Revision | Directory | Content
-
-"""
-Possible release target types
-"""
-enum ReleaseTargetType {
-  release
-  revision
-  content
-  directory
-}
-
 """
 A release object
 """
@@ -716,14 +717,44 @@
   date: Date
 
   """
-  Type of release target
+  Release target node
+  """
+  target: ReleaseTarget
+}
+
+"""
+Possible release target types
+"""
+enum ReleaseTargetType {
+  release
+  revision
+  content
+  directory
+}
+
+"""
+Possible release target nodes
+"""
+union ReleaseTargetNode = Release | Revision | Directory | Content
+
+"""
+A release target object
+"""
+type ReleaseTarget {
+  """
+  Release target type
   """
-  targetType: ReleaseTargetType
+  type: ReleaseTargetType!
 
   """
-  Release target object
+  Release target identifier
   """
-  target: ReleaseTarget
+  identifier: ID!
+
+  """
+  Release target SWH object
+  """
+  node: ReleaseTargetNode
 }
 
 """
@@ -766,10 +797,26 @@
   node: DirectoryEntry
 }
 
+
+"""
+A directory entry object
+"""
+type DirectoryEntry {
+  """
+  The directory entry name
+  """
+  name: BinaryString
+
+  """
+  Directory entry target node
+  """
+  target: DirectoryEntryTarget
+}
+
 """
 Possible directory entry target objects
 """
-union DirectoryEntryTarget = Directory | Content | Revision
+union DirectoryEntryTargetNode = Directory | Content | Revision
 
 """
 Possible directory entry types
@@ -781,25 +828,26 @@
 }
 
 """
-A directory entry object
+A directoryentry target object
 """
-type DirectoryEntry {
+type DirectoryEntryTarget {
   """
-  The directory entry name
+  Directoryentry target type
   """
-  name: BinaryString
+  type: DirectoryEntryTargetType!
 
   """
-  Directory entry object type; can be file, dir or rev
+  Directoryentry target SWHID
   """
-  targetType: DirectoryEntryTargetType
+  identifier: ID!
 
   """
-  Directory entry target object
+  Directoryentry target SWH object
   """
-  target: DirectoryEntryTarget
+  node: DirectoryEntryTargetNode
 }
 
+
 """
 A directory object
 """
@@ -974,8 +1022,20 @@
   node: SearchResult
 }
 
-union SearchResultTarget = Origin | Revision | Release | Content | Directory | Snapshot
 
+"""
+A SearchResult object
+"""
+type SearchResult {
+  """
+  Result target object
+  """
+  target: SearchResultTarget
+}
+
+"""
+Possible search result target types
+"""
 enum SearchResultTargetType {
   origin
   revision
@@ -986,18 +1046,28 @@
 }
 
 """
-A SearchResult object
+Possible search result target nodes
 """
-type SearchResult {
+union SearchResultTargetNode = Origin | Revision | Release | Content | Directory | Snapshot
+
+"""
+A search target object
+"""
+type SearchResultTarget {
   """
-  Result target type
+  Search result target type
   """
-  targetType: SearchResultTargetType
+  type: SearchResultTargetType
 
   """
-  Result target object
+  Search result target identifier
   """
-  target: SearchResultTarget
+  identifier: ID!
+
+  """
+  Search result target SWH object
+  """
+  node: SearchResultTargetNode
 }
 
 """
diff --git a/swh/graphql/tests/functional/test_branch_connection.py b/swh/graphql/tests/functional/test_branch_connection.py
--- a/swh/graphql/tests/functional/test_branch_connection.py
+++ b/swh/graphql/tests/functional/test_branch_connection.py
@@ -19,31 +19,33 @@
             endCursor
           }
           nodes {
-            targetType
             name {
               text
             }
             target {
-              __typename
-              ...on Branch {
-                name {
-                  text
+              type
+              node {
+                __typename
+                ...on Branch {
+                  name {
+                    text
+                  }
+                }
+                ...on Revision {
+                  swhid
+                }
+                ...on Release {
+                  swhid
+                }
+                ...on Content {
+                  swhid
+                }
+                ...on Directory {
+                  swhid
+                }
+                ...on Snapshot {
+                  swhid
                 }
-              }
-              ...on Revision {
-                swhid
-              }
-              ...on Release {
-                swhid
-              }
-              ...on Content {
-                swhid
-              }
-              ...on Directory {
-                swhid
-              }
-              ...on Snapshot {
-                swhid
               }
             }
           }
@@ -63,10 +65,12 @@
     assert node == {
         "name": {"text": "target/revision"},
         "target": {
-            "__typename": "Revision",
-            "swhid": "swh:1:rev:66c7c1cd9673275037140f2abff7b7b11fc9439c",
+            "type": "revision",
+            "node": {
+                "__typename": "Revision",
+                "swhid": "swh:1:rev:66c7c1cd9673275037140f2abff7b7b11fc9439c",
+            },
         },
-        "targetType": "revision",
     }
 
 
@@ -76,8 +80,10 @@
     node = data["snapshot"]["branches"]["nodes"][0]
     assert node == {
         "name": {"text": "target/alias"},
-        "target": {"__typename": "Branch", "name": {"text": "target/revision"}},
-        "targetType": "alias",
+        "target": {
+            "type": "alias",
+            "node": {"__typename": "Branch", "name": {"text": "target/revision"}},
+        },
     }
 
 
@@ -96,8 +102,8 @@
     data, _ = get_branches(client, swhid=swhid, first=10, types=[filter_type])
     assert len(data["snapshot"]["branches"]["nodes"]) == count
     for node in data["snapshot"]["branches"]["nodes"]:
-        assert node["target"]["__typename"] == target_type
-        assert node["target"]["swhid"].startswith(swhid_pattern)
+        assert node["target"]["node"]["__typename"] == target_type
+        assert node["target"]["node"]["swhid"].startswith(swhid_pattern)
 
 
 @pytest.mark.parametrize(
diff --git a/swh/graphql/tests/functional/test_content.py b/swh/graphql/tests/functional/test_content.py
--- a/swh/graphql/tests/functional/test_content.py
+++ b/swh/graphql/tests/functional/test_content.py
@@ -233,11 +233,14 @@
         swhid
         entries(first: 2) {
           nodes {
-            targetType
             target {
-              ...on Content {
-                swhid
-                length
+              type
+              identifier
+              node {
+                ...on Content {
+                  swhid
+                  length
+                }
               }
             }
           }
@@ -248,6 +251,10 @@
     data, _ = utils.get_query_response(client, query_str, swhid=directory_swhid)
     content_obj = data["directory"]["entries"]["nodes"][1]["target"]
     assert content_obj == {
-        "length": 4,
-        "swhid": "swh:1:cnt:86bc6b377e9d25f9d26777a4a28d08e63e7c5779",
+        "type": "content",
+        "identifier": "86bc6b377e9d25f9d26777a4a28d08e63e7c5779",
+        "node": {
+            "length": 4,
+            "swhid": "swh:1:cnt:86bc6b377e9d25f9d26777a4a28d08e63e7c5779",
+        },
     }
diff --git a/swh/graphql/tests/functional/test_directory.py b/swh/graphql/tests/functional/test_directory.py
--- a/swh/graphql/tests/functional/test_directory.py
+++ b/swh/graphql/tests/functional/test_directory.py
@@ -65,8 +65,10 @@
       release(swhid: $swhid) {
         swhid
         target {
-          ...on Directory {
-            swhid
+          node {
+            ...on Directory {
+              swhid
+            }
           }
         }
       }
@@ -77,7 +79,7 @@
         query_str,
         swhid="swh:1:rel:ee4d20e80af850cc0f417d25dc5073792c5010d2",
     )
-    assert data["release"]["target"] == {
+    assert data["release"]["target"]["node"] == {
         "swhid": "swh:1:dir:0505050505050505050505050505050505050505"
     }
 
diff --git a/swh/graphql/tests/functional/test_directory_entry.py b/swh/graphql/tests/functional/test_directory_entry.py
--- a/swh/graphql/tests/functional/test_directory_entry.py
+++ b/swh/graphql/tests/functional/test_directory_entry.py
@@ -30,10 +30,13 @@
         name {
           text
         }
-        targetType
         target {
-          ...on Content {
-            swhid
+          type
+          identifier
+          node {
+            ...on Content {
+              swhid
+            }
           }
         }
       }
@@ -59,16 +62,18 @@
         name {
           text
         }
-        targetType
         target {
-          ...on Content {
-            swhid
-          }
-          ...on Directory {
-            swhid
-          }
-          ...on Revision {
-            swhid
+          type
+          node {
+            ...on Content {
+              swhid
+            }
+            ...on Directory {
+              swhid
+            }
+            ...on Revision {
+              swhid
+            }
           }
         }
       }
@@ -96,8 +101,12 @@
             )
         assert data["directoryEntry"] == {
             "name": {"text": entry["name"].decode()},
-            "target": {"swhid": str(swhid)} if swhid else None,
-            "targetType": get_target_type(entry["type"]),
+            "target": {
+                "type": get_target_type(entry["type"]),
+                "node": {"swhid": str(swhid)},
+            }
+            if swhid
+            else {"type": get_target_type(entry["type"]), "node": None},
         }
 
 
@@ -109,10 +118,12 @@
         swhid
         entries {
           nodes {
-            targetType
             name {
               text
             }
+            target {
+              type
+            }
           }
         }
       }
@@ -122,7 +133,10 @@
     directory_entries = data["directory"]["entries"]["nodes"]
     assert len(directory_entries) == len(directory.entries)
     output = [
-        {"name": {"text": de.name.decode()}, "targetType": get_target_type(de.type)}
+        {
+            "name": {"text": de.name.decode()},
+            "target": {"type": get_target_type(de.type)},
+        }
         for de in directory.entries
     ]
     for each_entry in output:
@@ -140,10 +154,12 @@
             swhid
             entries(nameInclude: $nameInclude) {
               nodes {
-                targetType
                 name {
                   text
                 }
+                target {
+                  type
+                }
               }
             }
           }
@@ -157,7 +173,7 @@
         )
         for entry in data["directory"]["entries"]["nodes"]:
             assert name_include in entry["name"]["text"]
-            assert entry["targetType"] == get_target_type(dir_entry["type"])
+            assert entry["target"]["type"] == get_target_type(dir_entry["type"])
 
 
 def test_directory_entry_connection_filter_by_name_special_chars(client):
@@ -167,7 +183,6 @@
       directory(swhid: $swhid) {
         entries(nameInclude: $nameInclude) {
           nodes {
-            targetType
             name {
               text
             }
@@ -184,5 +199,4 @@
     )
     assert data["directory"]["entries"]["nodes"][0] == {
         "name": {"text": "ßßétEÉt"},
-        "targetType": "content",
     }
diff --git a/swh/graphql/tests/functional/test_release_node.py b/swh/graphql/tests/functional/test_release_node.py
--- a/swh/graphql/tests/functional/test_release_node.py
+++ b/swh/graphql/tests/functional/test_release_node.py
@@ -50,7 +50,9 @@
             base64
           }
         }
-        targetType
+        target {
+          type
+        }
       }
     }
     """
@@ -81,7 +83,9 @@
         }
         if release.date
         else None,
-        "targetType": release.target_type.value,
+        "target": {
+            "type": release.target_type.value,
+        },
     }
 
 
@@ -104,19 +108,21 @@
     query_str = """
     query getRelease($swhid: SWHID!) {
       release(swhid: $swhid) {
-        targetType
         target {
-          ...on Revision {
-            swhid
-          }
-          ...on Release {
-            swhid
-          }
-          ...on Directory {
-            swhid
-          }
-          ...on Content {
-            swhid
+          type
+          node {
+            ...on Revision {
+              swhid
+            }
+            ...on Release {
+              swhid
+            }
+            ...on Directory {
+              swhid
+            }
+            ...on Content {
+              swhid
+            }
           }
         }
       }
@@ -127,16 +133,19 @@
     )
 
     if release_with_target.target_type == ObjectType.REVISION:
-        target_swhid = get_revisions()[0].swhid()
+        target = get_revisions()[0]
     elif release_with_target.target_type == ObjectType.RELEASE:
-        target_swhid = get_releases()[0].swhid()
+        target = get_releases()[0]
     elif release_with_target.target_type == ObjectType.DIRECTORY:
-        target_swhid = get_directories()[0].swhid()
+        target = get_directories()[0]
     elif release_with_target.target_type == ObjectType.CONTENT:
-        target_swhid = get_contents()[0].swhid()
+        target = get_contents()[0]
     assert data["release"] == {
-        "targetType": release_with_target.target_type.value,
-        "target": {"swhid": str(target_swhid)},
+        "target": {
+            "type": release_with_target.target_type.value,
+            # "identifier": target.id.hex(),
+            "node": {"swhid": str(target.swhid())},
+        },
     }
 
 
@@ -150,22 +159,25 @@
     query_str = """
     query getRelease($swhid: SWHID!) {
       release(swhid: $swhid) {
-        targetType
         target {
-          ...on Revision {
-            swhid
-            message {
-              text
+          type
+          identifier
+          node {
+            ...on Revision {
+              swhid
+              message {
+                text
+              }
+            }
+            ...on Release {
+              swhid
+            }
+            ...on Directory {
+              swhid
+            }
+            ...on Content {
+              swhid
             }
-          }
-          ...on Release {
-            swhid
-          }
-          ...on Directory {
-            swhid
-          }
-          ...on Content {
-            swhid
           }
         }
       }
@@ -174,10 +186,13 @@
     data, _ = utils.get_query_response(client, query_str, swhid=str(swhid))
     assert data["release"] == {
         "target": {
-            "message": {"text": "hello"},
-            "swhid": str(get_revisions()[0].swhid()),
+            "type": "revision",
+            "identifier": get_revisions()[0].id.hex(),
+            "node": {
+                "message": {"text": "hello"},
+                "swhid": str(get_revisions()[0].swhid()),
+            },
         },
-        "targetType": "revision",
     }
 
 
diff --git a/swh/graphql/tests/functional/test_revision.py b/swh/graphql/tests/functional/test_revision.py
--- a/swh/graphql/tests/functional/test_revision.py
+++ b/swh/graphql/tests/functional/test_revision.py
@@ -137,10 +137,12 @@
       snapshot(swhid: $swhid) {
         branches(first: 1, types: [revision]) {
           nodes {
-            targetType
             target {
-              ...on Revision {
-                swhid
+              type
+              node {
+                ...on Revision {
+                  swhid
+                }
               }
             }
           }
@@ -149,7 +151,7 @@
     }
     """
     data, _ = utils.get_query_response(client, query_str, swhid=snapshot_swhid)
-    revision_obj = data["snapshot"]["branches"]["nodes"][0]["target"]
+    revision_obj = data["snapshot"]["branches"]["nodes"][0]["target"]["node"]
     assert revision_obj == {
         "swhid": "swh:1:rev:66c7c1cd9673275037140f2abff7b7b11fc9439c"
     }
diff --git a/swh/graphql/tests/functional/test_search.py b/swh/graphql/tests/functional/test_search.py
--- a/swh/graphql/tests/functional/test_search.py
+++ b/swh/graphql/tests/functional/test_search.py
@@ -11,12 +11,14 @@
     query doSearch($query: String!, $first: Int!) {
       search(query: $query, first: $first) {
         nodes {
-          targetType
           target {
-            ...on Origin {
-              url
-              latestVisit {
-                date
+            type
+            node {
+              ...on Origin {
+                url
+                latestVisit {
+                  date
+                }
               }
             }
           }
@@ -35,10 +37,12 @@
             "nodes": [
                 {
                     "target": {
-                        "url": "https://somewhere.org/den/fox",
-                        "latestVisit": {"date": "2018-11-27T17:20:39+00:00"},
+                        "type": "origin",
+                        "node": {
+                            "url": "https://somewhere.org/den/fox",
+                            "latestVisit": {"date": "2018-11-27T17:20:39+00:00"},
+                        },
                     },
-                    "targetType": "origin",
                 }
             ],
             "pageInfo": {"endCursor": "MQ==", "hasNextPage": True},
@@ -51,7 +55,9 @@
     query doSearch($query: String!, $first: Int!) {
       search(query: $query, first: $first) {
         nodes {
-          targetType
+          target {
+            type
+          }
         }
         pageInfo {
           hasNextPage
diff --git a/swh/graphql/tests/functional/test_snapshot_node.py b/swh/graphql/tests/functional/test_snapshot_node.py
--- a/swh/graphql/tests/functional/test_snapshot_node.py
+++ b/swh/graphql/tests/functional/test_snapshot_node.py
@@ -18,10 +18,12 @@
         swhid
         branches(first:5) {
           nodes {
-            targetType
             name {
               text
             }
+            target {
+              type
+            }
           }
         }
       }
diff --git a/swh/graphql/tests/functional/test_swhid_resolve.py b/swh/graphql/tests/functional/test_swhid_resolve.py
--- a/swh/graphql/tests/functional/test_swhid_resolve.py
+++ b/swh/graphql/tests/functional/test_swhid_resolve.py
@@ -19,7 +19,9 @@
     query_str = """
     query resolve($swhid: SWHID!) {
       resolveSWHID(swhid: $swhid) {
-        targetType
+        target {
+          type
+        }
       }
     }
     """
@@ -43,7 +45,9 @@
     query_str = """
     query resolve($swhid: SWHID!) {
       resolveSWHID(swhid: $swhid) {
-        targetType
+        target {
+          type
+        }
       }
     }
     """
@@ -57,11 +61,13 @@
     query_str = """
     query resolve($swhid: SWHID!) {
       resolveSWHID(swhid: $swhid) {
-        targetType
         target {
-          __typename
-          ... on Snapshot {
-            swhid
+          type
+          node {
+            __typename
+            ... on Snapshot {
+              swhid
+            }
           }
         }
       }
@@ -72,10 +78,12 @@
         "resolveSWHID": [
             {
                 "target": {
-                    "__typename": "Snapshot",
-                    "swhid": str(snapshot.swhid()),
+                    "type": "snapshot",
+                    "node": {
+                        "__typename": "Snapshot",
+                        "swhid": str(snapshot.swhid()),
+                    },
                 },
-                "targetType": "snapshot",
             }
         ]
     }
@@ -86,11 +94,13 @@
     query_str = """
     query resolve($swhid: SWHID!) {
       resolveSWHID(swhid: $swhid) {
-        targetType
         target {
-          __typename
-          ... on Revision {
-            swhid
+          type
+          node {
+            __typename
+            ... on Revision {
+              swhid
+            }
           }
         }
       }
@@ -101,10 +111,12 @@
         "resolveSWHID": [
             {
                 "target": {
-                    "__typename": "Revision",
-                    "swhid": str(revision.swhid()),
-                },
-                "targetType": "revision",
+                    "type": "revision",
+                    "node": {
+                        "__typename": "Revision",
+                        "swhid": str(revision.swhid()),
+                    },
+                }
             }
         ]
     }
@@ -115,11 +127,13 @@
     query_str = """
     query resolve($swhid: SWHID!) {
       resolveSWHID(swhid: $swhid) {
-        targetType
-        target {
-          __typename
-          ... on Release {
-            swhid
+         target {
+          type
+          node {
+            __typename
+            ... on Release {
+              swhid
+            }
           }
         }
       }
@@ -130,10 +144,12 @@
         "resolveSWHID": [
             {
                 "target": {
-                    "__typename": "Release",
-                    "swhid": str(release.swhid()),
-                },
-                "targetType": "release",
+                    "type": "release",
+                    "node": {
+                        "__typename": "Release",
+                        "swhid": str(release.swhid()),
+                    },
+                }
             }
         ]
     }
@@ -144,11 +160,13 @@
     query_str = """
     query resolve($swhid: SWHID!) {
       resolveSWHID(swhid: $swhid) {
-        targetType
         target {
-          __typename
-          ... on Directory {
-            swhid
+          type
+          node {
+            __typename
+            ... on Directory {
+              swhid
+            }
           }
         }
       }
@@ -159,10 +177,12 @@
         "resolveSWHID": [
             {
                 "target": {
-                    "__typename": "Directory",
-                    "swhid": str(directory.swhid()),
-                },
-                "targetType": "directory",
+                    "type": "directory",
+                    "node": {
+                        "__typename": "Directory",
+                        "swhid": str(directory.swhid()),
+                    },
+                }
             }
         ]
     }
@@ -173,11 +193,13 @@
     query_str = """
     query resolve($swhid: SWHID!) {
       resolveSWHID(swhid: $swhid) {
-        targetType
         target {
-          __typename
-          ... on Content {
-            swhid
+          type
+          node {
+            __typename
+            ... on Content {
+              swhid
+            }
           }
         }
       }
@@ -188,10 +210,12 @@
         "resolveSWHID": [
             {
                 "target": {
-                    "__typename": "Content",
-                    "swhid": str(content.swhid()),
-                },
-                "targetType": "content",
+                    "type": "content",
+                    "node": {
+                        "__typename": "Content",
+                        "swhid": str(content.swhid()),
+                    },
+                }
             }
         ]
     }
diff --git a/swh/graphql/tests/unit/resolvers/test_resolvers.py b/swh/graphql/tests/unit/resolvers/test_resolvers.py
--- a/swh/graphql/tests/unit/resolvers/test_resolvers.py
+++ b/swh/graphql/tests/unit/resolvers/test_resolvers.py
@@ -65,58 +65,6 @@
         # assert the right object is returned
         assert isinstance(connection_obj, connection_cls)
 
-    @pytest.mark.parametrize(
-        "branch_type, node_cls",
-        [
-            ("revision", resolvers.revision.TargetRevisionNode),
-            ("release", resolvers.release.TargetReleaseNode),
-            ("directory", resolvers.directory.TargetDirectoryNode),
-            ("content", resolvers.content.TargetContentNode),
-            ("snapshot", resolvers.snapshot.TargetSnapshotNode),
-        ],
-    )
-    def test_snapshot_branch_target_resolver(
-        self, mocker, dummy_node, branch_type, node_cls
-    ):
-        obj = mocker.Mock(targetType=branch_type)
-        mock_get = mocker.patch.object(node_cls, "_get_node", return_value=dummy_node)
-        node_obj = rs.snapshot_branch_target_resolver(obj, None)
-        assert isinstance(node_obj, node_cls)
-        assert mock_get.assert_called
-
-    @pytest.mark.parametrize(
-        "target_type, node_cls",
-        [
-            ("revision", resolvers.revision.TargetRevisionNode),
-            ("release", resolvers.release.TargetReleaseNode),
-            ("directory", resolvers.directory.TargetDirectoryNode),
-            ("content", resolvers.content.TargetContentNode),
-        ],
-    )
-    def test_release_target_resolver(self, mocker, dummy_node, target_type, node_cls):
-        obj = mocker.Mock(targetType=target_type)
-        mock_get = mocker.patch.object(node_cls, "_get_node", return_value=dummy_node)
-        node_obj = rs.release_target_resolver(obj, None)
-        assert isinstance(node_obj, node_cls)
-        assert mock_get.assert_called
-
-    @pytest.mark.parametrize(
-        "target_type, node_cls",
-        [
-            ("directory", resolvers.directory.TargetDirectoryNode),
-            ("content", resolvers.content.TargetContentNode),
-            ("revision", resolvers.revision.TargetRevisionNode),
-        ],
-    )
-    def test_directory_entry_target_resolver(
-        self, mocker, dummy_node, target_type, node_cls
-    ):
-        obj = mocker.Mock(targetType=target_type)
-        mock_get = mocker.patch.object(node_cls, "_get_node", return_value=dummy_node)
-        node_obj = rs.directory_entry_target_resolver(obj, None)
-        assert isinstance(node_obj, node_cls)
-        assert mock_get.assert_called
-
     def test_union_resolver(self, mocker):
         obj = mocker.Mock()
         obj.is_type_of.return_value = "test"
diff --git a/swh/graphql/tests/unit/resolvers/test_scalars.py b/swh/graphql/tests/unit/resolvers/test_scalars.py
--- a/swh/graphql/tests/unit/resolvers/test_scalars.py
+++ b/swh/graphql/tests/unit/resolvers/test_scalars.py
@@ -12,7 +12,7 @@
 
 
 def test_serialize_id():
-    assert scalars.serialize_id("test") == "74657374"
+    assert scalars.serialize_id("test") == "test"
     assert scalars.serialize_id(b"test") == "74657374"