ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.25k stars 744 forks source link

Extending edge type with relationship data #5036

Open BohdanMaslowski opened 2 years ago

BohdanMaslowski commented 2 years ago

Is your feature request related to a problem?

We use Hot Chocolate on top of EF Core with various entities being in many-to-many relationship with additional relationship data in the join entity types (eg. PostTag.PublicationDate in the sample below).

We would like to add the relationship data to the edge types (as suggested here), but couldn't figure out any way to achieve it.

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    [UsePaging]
    public ICollection<Tag> Tags { get; set; }

    public ICollection<PostTag> PostTags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    [UsePaging]
    public ICollection<Post> Posts { get; set; }

    public ICollection<PostTag> PostTags { get; set; }
}

public class PostTag
{
    public DateTime PublicationDate { get; set; } // should be included in the edge type

    public int PostId { get; set; }
    public Post Post { get; set; }

    public string TagId { get; set; }
    public Tag Tag { get; set; }
}

(we also use projections, filtering and sorting)

The solution you'd like

Any solution, that would allow extending edge types with data from join entity type. For instance placing the UsePaging attribute on the navigation property of the join entity and specifying a selector for node.

public class Tag
{
    public string TagId { get; set; }

    // public ICollection<Post> Posts { get; set; }

    [UsePaging(NodeSelector = "Post"]
    public ICollection<PostTag> Posts { get; set; }
}

Product

Hot Chocolate

michaelstaib commented 2 years ago

@PascalSenn this is something that is at the moment possible but difficult to do. We need to have a look at how we can simplify this.

Essentially on the EdgeType, we want to expose metadata about the connection...

type FriendsEdge {
   node: User
   cursor: String!
   becameFriendsOn: DateTime # <---- metadata
}
PascalSenn commented 2 years ago

@michaelstaib i will need this in the elastic extensions soon too

BohdanMaslowski commented 2 years ago

We use this pattern in our current API (which we want to migrate to HC without breaking changes) - relationship data are encapsulated within EdgeInfo type, which is then used in both directional edge types:

type CasePersonConnection {
  nodes: [Person]
  edges: [CasePersonEdge]
}
type CasePersonEdge {
  edgeInfo: CasePersonEdgeInfo
  node: Person
}
type PersonCaseConnection {
  nodes: [Case]
  edges: [PersonCaseEdge]
}
type PersonCaseEdge {
  edgeInfo: CasePersonEdgeInfo
  node: Case
}
type CasePersonEdgeInfo { #relationship data (shared type)
  processRole: CasePersonRole
  materialRole: CasePersonRole
  isMainPayer: Boolean
  isMainClient: Boolean
}

It would be great, if this is also supported.

tobias-tengler commented 1 year ago

Just wanted to chime in that this would also be something that I would be interested in. It would be great if we could just create our own Edge Type that implements the Edge contract (cursor & node) and pass this collection of edges to the Connection<T> returned from the resolver. The type of the edges could be specified similar to the node type: [UsePaging<NodeType, EdgeType>]

michaelstaib commented 1 year ago

@tobias-tengler why not use type extensions?

[ExtendObjectType<IEdge<Bar>>]
public class EdgeExtensions
{
    public string Foo([Parent] IEdge<Bar> edge) => "bay"
}
BohdanMaslowski commented 1 year ago

@michaelstaib doesn't solve our problem - we need to access properties of the join entity type, and there's no way to access them from the target/node entity.

michaelstaib commented 1 year ago

@BohdanMaslowski what do you mean by that? The parent object that has the field?

{
    foo {                       # <-- this
      bars {                   # <-- or this
          nodes {
             ...
         }
      }
   }
}
tobias-tengler commented 1 year ago

We mean something like

{
  me {
    friends { # FriendsConnection
      edges { # [FriendEdge]
        cursor
        since # This field comes from a JOIN table or some other connection between entities
        node { # User
          name
      }
    }
  }
}
BohdanMaslowski commented 1 year ago

Yes, in the example at the beginning, there is a Post ⇔ PostTag ⇔ Tag structure in EF, but I don't want to expose the join entity type PostTag as a type in GQL schema, but to include some of its fields in the PostTagEdge type.

kwiatopl commented 12 months ago

Any update on this topic?