subgraph icon indicating copy to clipboard operation
subgraph copied to clipboard

Scheme & Proposal Base Interfaces

Open dOrgJelli opened this issue 4 years ago • 3 comments

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]()

dOrgJelli avatar Mar 31 '20 15:03 dOrgJelli

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

ben-kaufman avatar Apr 20 '20 13:04 ben-kaufman

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

dOrgJelli avatar Apr 20 '20 16:04 dOrgJelli

https://github.com/graphprotocol/graph-cli/pull/477

ben-kaufman avatar Apr 21 '20 10:04 ben-kaufman