Type Inheritance (SpecialUser extends User)

Hiya!

We are exploring some options for extending types in our supergraphs in order to provide an interface-y feel without having to redefine fields and resolvers all over the place.

The use case goes something like this: we have a type SpecialUser that is a User with additional properties:

User {
  id
  name
}

SpecialUser {
  specialProperty
}

In this case we have User coming from a Users Subgraph and SpecialUser coming from a Special Subgraph. What we’re hoping we can do is simply have some kind of type extension which allows us to query like this:

specialUser {
  id
  name
  specialProperty
}

Where id/name are resolved in the Users Subgraph and specialProperty is resolved in the Special Subgraph. What we want to avoid is being able to query specialProperty on a regular User because depending on the context that property may or may not exist.

We’re using NestJS with TypeGraphQL so if we were just inside a single subgraph we could use inheritance but since we’re crossing domains here I’m not really sure what our options are, if any.

Has anyone encountered this before or have any ideas if/how we could accomplish such a design?

Here I am talking to myself as usual, but perhaps the best solution here is to do the inverse? Extend SpecialUser from the Users Subgraph? It’s kind of annoying to have to do that since it makes our Users Subgraph aware of every type that could inherit from the User… but it will work.

@adamlarsonlee This is an interesting question I see come up with NestJS every so often, and this thread is a great example of implementing interface-like elements (e.g. Entities). There is a layer of depth that can be added once the initial state of your entities is set (more info on our dev docs here), overall each circumstance is going to vary in requirements for a product’s use-case.

You are correct in that extending SpecialUser from the Users subgraph is going to result in an inherited data structure. The end result expected is to add a specialProperty to the existing Users fields (id, name). Happy to discuss further!

Yep - that all makes sense. Maybe if I extend my example a little bit what I’m asking will make more sense:


User {
  name
  email
}

AdminUser {
  ...User
  ...someAdminProperties
}

CustomerUser {
   ...User
   ...someCustomerProperties
}

UserWithContext {
   ...User
   ...someContextProperties
}

So my goal here is to not have to extend a type every time in my Users subgraph. If for some reason I have some type down the road that simply adds additional context to a User I want to be able to inherit all of those resolved fields from User without making a change there. Does that make more sense? I know how this can be done in a traditional sense I’m just trying to avoid the extra legwork through some kind of inheritance model where my types in other subgraphs can inherit from User and automatically have those properties resolved.

I’ve decided to go with what I’m calling reverse inheritance. This is implemented with an abstract resolver and object type where implementing a new type that needs to inherit these properties only requires a new resolver that inherits from the abstract resolver and a type that inherits from the abstract type. This is accomplished with NestJs and typegraphql inheritance. Entities are retrieved with a dataloader and every property has its own field resolver.