Graphene: Allow elements in edges to be marked as NonNull

Created on 17 May 2019  Â·  8Comments  Â·  Source: graphql-python/graphene

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!]!
}
✨ enhancement

Most helpful comment

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?

All 8 comments

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.

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

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?

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!
}

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.

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 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.

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mandx picture mandx  Â·  4Comments

jloveric picture jloveric  Â·  4Comments

danpalmer picture danpalmer  Â·  4Comments

junchiz picture junchiz  Â·  3Comments

ghoshabhi picture ghoshabhi  Â·  3Comments