Nodes
=====

A ``Node`` is an Interface provided by ``graphene.relay`` that contains
a single field ``id`` (which is a ``ID!``). Any object that inherits
from it has to implement a ``get_node`` method for retrieving a
``Node`` by an *id*.


Quick example
-------------

Example usage (taken from the `Starwars Relay example`_):

.. code:: python

    class Ship(graphene.ObjectType):
        '''A ship in the Star Wars saga'''
        class Meta:
            interfaces = (relay.Node, )

        name = graphene.String(description='The name of the ship.')

        @classmethod
        def get_node(cls, info, id):
            return get_ship(id)

The ``id`` returned by the ``Ship`` type when you query it will be a
scalar which contains enough info for the server to know its type and
its id.

For example, the instance ``Ship(id=1)`` will return ``U2hpcDox`` as the
id when you query it (which is the base64 encoding of ``Ship:1``), and
which could be useful later if we want to query a node by its id.


Custom Nodes
------------

You can use the predefined ``relay.Node`` or you can subclass it, defining
custom ways of how a node id is encoded (using the ``to_global_id`` method in the class)
or how we can retrieve a Node given a encoded id (with the ``get_node_from_global_id`` method).

Example of a custom node:

.. code:: python

    class CustomNode(Node):

        class Meta:
            name = 'Node'

        @staticmethod
        def to_global_id(type_, id):
            return f"{type_}:{id}"

        @staticmethod
        def get_node_from_global_id(info, global_id, only_type=None):
            type_, id = global_id.split(':')
            if only_type:
                # We assure that the node type that we want to retrieve
                # is the same that was indicated in the field type
                assert type_ == only_type._meta.name, 'Received not compatible node.'

            if type_ == 'User':
                return get_user(id)
            elif type_ == 'Photo':
                return get_photo(id)


The ``get_node_from_global_id`` method will be called when ``CustomNode.Field`` is resolved.


Accessing node types
--------------------

If we want to retrieve node instances from a ``global_id`` (scalar that identifies an instance by it's type name and id),
we can simply do ``Node.get_node_from_global_id(info, global_id)``.

In the case we want to restrict the instance retrieval to a specific type, we can do:
``Node.get_node_from_global_id(info, global_id, only_type=Ship)``. This will raise an error
if the ``global_id`` doesn't correspond to a Ship type.


Node Root field
---------------

As is required in the `Relay specification`_, the server must implement
a root field called ``node`` that returns a ``Node`` Interface.

For this reason, ``graphene`` provides the field ``relay.Node.Field``,
which links to any type in the Schema which implements ``Node``.
Example usage:

.. code:: python

    class Query(graphene.ObjectType):
        # Should be CustomNode.Field() if we want to use our custom Node
        node = relay.Node.Field()

.. _Relay specification: https://facebook.github.io/relay/docs/graphql-relay-specification.html
.. _Starwars Relay example: https://github.com/graphql-python/graphene/blob/master/examples/starwars_relay/schema.py
