Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scheme & Proposal Base Interfaces #505

Open
dOrgJelli opened this issue Mar 31, 2020 · 3 comments
Open

Scheme & Proposal Base Interfaces #505

dOrgJelli opened this issue Mar 31, 2020 · 3 comments

Comments

@dOrgJelli
Copy link
Contributor

Current Behavior

Currently, the subgraph schema's solution to adding new types of scheme & proposal types requiring changing the base type (ControllerScheme & Proposal). This is because each derived type is added as a nullable field in the base type like so:

type Base {
  id: ID!
  baseProp: String!
  typeName: String!

  # Implementations
  derivedA: DerivedA
  derivedB: DerivedB
}

In order to query all Base entities, you'd have to form a query like so:

bases {
  id
  baseProp
  typeName
  derivedA {
    # All derivedA props
    someFieldA
  }
  derivedB {
    # All derivedB props
    someFieldB
  }
}

And in order to know what sub type each entity in the collection is (derivedA or derivedB) you have to check the typeName, then access the correct field that corresponds to that entity:

if (res.typeName === "DerivedA") {
  res.derivedA.someFieldA
} else if (res.typeName === "DerivedB") {
  res.derivedB.someFieldB
}

Opinion

Doing things this way is a "non-standard" way of implementing polymorphism in GraphQL. Here are two ways in which GraphQL supports polymorphic types: https://www.apollographql.com/docs/apollo-server/schema/unions-interfaces/

Unions make sense for when types don't have shared properties. Interfaces make sense for when types have shared "base" properties.

Proposal

Utilize GraphQL interfaces for both Proposal and Scheme types. Here's an example of what this would look like:

interface Base {
  id: ID!
  baseProp: String!
}

type DerivedA implements Base {
  id: ID!
  baseProp: String!
  someFieldA: String!
}

type DerivedB implements Base {
  id: ID!
  baseProp: String!
  someFieldB: String!
}
// Generate an array of fragments for each known type
const fragments = []
for (const derived of DerivedTypes) {
  fragments.push(derived.Fields)
}
query Bases {
  bases {
    __typename
    id
    baseProp

    # Can be generated from a list of known types
    # and appended to the query string, making this code
    # open for extension, closed for modification
    ... on DerivedA {
      DerivedAProps
    }
    ... on DerivedB {
      DerivedBProps
    }
  }
}
${fragments}

Then we can instantiate the different types from __typename:

new DerivedTypes[res.__typename]()
@ben-kaufman
Copy link
Contributor

This is currently blocked due to a Graph Node bug. I will follow up with them on that and return to it when possible.

@dOrgJelli
Copy link
Contributor Author

Awesome, thanks so much @ben-kaufman! Out of curiosity, what's the bug?

@ben-kaufman
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants