graphene icon indicating copy to clipboard operation
graphene copied to clipboard

Allow elements in edges to be marked as NonNull

Open Jaspaul opened this issue 6 years ago • 12 comments

It seems that an edge, within the connection edges, cannot be marked as NonNull. I believe this is the result of https://github.com/graphql-python/graphene/blob/master/graphene/relay/connection.py#L103 where the edges field is hard coded to NonNull(List(edge)).

...
"edges",
Field(
    NonNull(List(edge)),
    description="Contains the nodes in this connection.",
),
...

This request is motivated by the following use case where we have a collection of objects, which we can guarantee are never null.

See the following for a contrived example:

type PostConnection {
  pageInfo: PageInfo!
  edges: [PostEdge]!
}

Since we know that a PostEdge, if found, will never be null we would like to be able to specify the following:

type PostConnection {
  pageInfo: PageInfo!
  edges: [PostEdge!]!
}

Jaspaul avatar May 17 '19 14:05 Jaspaul

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 29 '19 20:07 stale[bot]

Is anyone doing work on this? Otherwise I might give it a go - it would make using elm-graphql with graphene relay much nicer :)

valberg avatar Aug 21 '19 21:08 valberg

Agreed, the way graphene handles automatic connection schema creation makes it difficult to specify non-null edges in the list.

At least in graphene's Connection class, it's nice that it considers if there's an Edge class already defined so I'm able to slightly do a bit of overriding to get the element non null. This will work:

class PostConnection(graphene.Connection):
    class Meta:
        node = Post

    class PostEdge(graphene.ObjectType):
        node = graphene.Field(Post, required=True)
        cursor = graphene.String(required=True)

    edges = graphene.List(graphene.NonNull(PostEdge), required=True)

Produces:

type PostConnection {
  """Pagination data for this connection."""
  pageInfo: PageInfo!
  edges: [PostEdge!]!
}

type PostEdge {
  node: Post!
  cursor: String!
}

You just gotta add the descriptions back in :)

Not sure what would be the nicest way to make the edge element non-null if doing such a thing was supported, maybe a meta field on the connection class?

ChazZeromus avatar Sep 09 '19 03:09 ChazZeromus

This can be nicely wrapped up in a subclass of Connection:

class NonNullConnection(graphene.relay.Connection, abstract=True):
    @classmethod
    def __init_subclass_with_meta__(cls, node, **kwargs):
        if not hasattr(cls, 'Edge'):
            _node = node
            class EdgeBase(graphene.ObjectType, name=f'{node._meta.name}Edge'):
                cursor = graphene.String(required=True)
                node = graphene.Field(_node, required=True)

            setattr(cls, 'Edge', EdgeBase)

        if not hasattr(cls, 'edges'):
            setattr(cls, 'edges',
                    graphene.List(graphene.NonNull(cls.Edge), required=True))

        super(NonNullConnection, cls).__init_subclass_with_meta__(
            node=_node,
            **kwargs
        )

Which can be used like this (assuming a type called User):

class UserConnection(NonNullConnection):
    class Meta:
        node = User

# or

UserConnection = type('UserConnection', (NonNullConnection,), {}, node=User)

And results in a schema like this:

type UserConnection {
  pageInfo: PageInfo!
  edges: [UserEdge!]!
}

type UserEdge {
  cursor: String!
  node: User!
}

p7g avatar Oct 02 '19 04:10 p7g

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Dec 31 '19 05:12 stale[bot]

Did this ever get solved in the library? I've been using @p7g's workaround for the last 6 months or so which is working fine - but this seems like a fairly important option now that everyone's using type generation with their GraphQL libraries.

jckw avatar Jun 10 '20 09:06 jckw

@jckw looks like it hasn't been resolved so I'm reopening. If you have time to create a PR that would be very helpful. Ideally specifying values as NonNull should be configurable since there conceivably cases where that is what you want. However they should be NonNull by default.

jkimbo avatar Jun 11 '20 10:06 jkimbo

Do you mean they should not be NonNull by default? Otherwise it might break backwards compatibility I think

jckw avatar Jun 27 '20 16:06 jckw

Anyone working on this? I regularly bump into this requirement.

ghost avatar Apr 15 '21 10:04 ghost

Bump 🙏🏻

Lekesoldat avatar Dec 09 '21 16:12 Lekesoldat

bump as well

alexaor avatar Dec 09 '21 18:12 alexaor

+1

pfcodes avatar Jun 22 '22 05:06 pfcodes

Client controlled nullability will fix this without having to interfere with the relay spec: https://github.com/graphql/graphql-spec/issues/867

erikwrede avatar Feb 15 '23 09:02 erikwrede

Released in 3.3.0. Please see the release notes to learn about the caveats of using this feature and why it might be worthwhile to wait for client controlled nullability instead.

erikwrede avatar Jul 26 '23 07:07 erikwrede