nicolasdao / graphql-s2s

Add GraphQL Schema support for type inheritance, generic typing, metadata decoration. Transpile the enriched GraphQL string schema into the standard string schema understood by graphql.js and the Apollo server client.
Other
187 stars 15 forks source link

Calculated Resolvers of Inherited Types Are Not Also Inherited #3

Closed geirman closed 5 years ago

geirman commented 6 years ago

Given this type definition

const typeDefs = `
   type User {
      id: ID!,
      firstName: String,
      lastName: String,
      fullName: String,
    }

    type Teammate inherits User {
      teamRole: String,
    }
`

And this resolver

const resolvers = {
  Query: {
    user: (_, { id }) => getUsers(id),
  },
  User: {
    fullName: user => `${user.firstName} ${user.lastName}`,
  },
}

If I query for fullName on the Teammate type, I get null. To fix this, I have to duplicate all User calculated in the Teammates type as well like this...

const resolvers = {
  Query: {
    user: (_, { id }) => getUsers(id),
  },
  User: {
    fullName: user => `${user.firstName} ${user.lastName}`,
  },
  Teammate: {
    fullName: user => `${user.firstName} ${user.lastName}`,
  },
}

I'd love it if Teammate inherited the calculated fields of User as well. Is that possible?

geirman commented 6 years ago

BTW: Thank you very much for your work. This is exactly what I needed, and I was a bit surprised that GraphQL itself did not support the concept of inheritance.

nicolasdao commented 6 years ago

Thanks a lot for your questions @geirman . I'll have a look at this asap.

geirman commented 6 years ago

Thanks Nicolas! I'm feeling pretty stoked about this package. I do have a couple other comments to make. I'll open other issues for those to keep things nice and tidy.

ryanmaxwell commented 6 years ago

Hi @geirman the newer versions of apollo server (since 2 I think) allow you to pass in an option to inherit the resolvers from interfaces (It seems like the issue is more to do with the execution of the resolvers rather than the generation of the schema which is what this project provides?)

May need a bit of shuffling of fullName into an interface, and making the class(es) implement it, but worth a try!

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
  inheritResolversFromInterfaces: true,
});
geirman commented 6 years ago

@ryanmaxwell thanks for that tip. Makes sense. I've so far not really understood the need for interfaces since most of the examples revolve around the ID! field, which has limited utility IMHO, but this could be an aha moment for me if this works.

I'm not 100% sure what a working example would look like off hand. Need to research interfaces more. But thanks for the nudge in the right direction.

purplemana commented 5 years ago

Came here looking for this. @geirman, I guess your use case should be solved with the help of this issue's PR. Basically, you could rewrite your code like:

const typeDefs = `
 interface User {
  id: ID!,
  firstName: String,
  lastName: String,
  fullName: String,
}

type Teammate inherits User implements User{
  teamRole: String,
}
`

This way User is still an interface and now the inheritResolversFromInterfaces option will make GraphQL look for resolving concrete type properties in User resolver.

Hope this helps.

nicolasdao commented 5 years ago

Closing this issue thanks to fix from @purplemana.