Apollo-server: resolver for interface

Created on 15 Mar 2018  路  3Comments  路  Source: apollographql/apollo-server


Is it possible to have resolver written once for the interface so all the implemented types could use it?

In below example, I have the IStat implemented by other types. It has resolver which comes as an instance of a class (or could be anything) and need to be converted to a string.

interface IStat {
    resource: String!
    name: String!
}

type FileStat implements IStat {
    resource: String!
    name: String!
    mtime: String!
}


type Content implements IStat {
    resource: String!
    name: String!
    value: String!
}

type StreamContent implements IStat {
    resource: String!
    name: String!
    value: String!
    encoding: String!
}

And in resolver

instead of (which is working)

export const resolver = {

    FileStat: {
        resource(root, args, ctx) {
            return convertToString(root.resource);
        },
    },
    Content: {
        resource(root, args, ctx) {
            return  convertToString(root.resource);
        },
    },
    StreamContent: {
        resource(root, args, ctx) {
            return  convertToString(root.resource);
        },
    },

}
````
to be following so we are writing `IStat` once and expected all the `types` implemented by it uses it. 

export const resolver = {

IStat: {
    resource(root, args, ctx) {
        return  convertToString(root.resource);
    },
},

}
````

鉀诧笍 feature

Most helpful comment

It's possible to inherit resolvers from an interface, but the more advanced use currently necessitates the use of makeExecutableSchema, which is also exported from apollo-server (and its variants). The key is to use inheritResolversFromInterfaces: true on makeExecutableSchema's options and pass the resulting schema to Apollo Server in place of typeDefs and resolvers.

I've mocked this up quickly in this Glitch, but here's the code, request and result:

import { ApolloServer, gql, makeExecutableSchema } from 'apollo-server';

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  interface IStat {
      resource: String!
      name: String!
  }

  type Content implements IStat {
      resource: String!
      name: String!
      value: String!
  }

  type StreamContent implements IStat {
      resource: String!
      name: String!
      value: String!
      encoding: String!
  }

  type Query {
    content: Content
    stream: StreamContent
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  IStat: {
    resource(parent, args, context, { parentType }) {
      return parentType.name; // (e.g. "Content", "StreamContent", etc.)
    }
  },
  Query: {
    content(parent, args, context, { returnType }) {
      return {
        name: "Some content",
        value: "6",
      };  
    },
    stream() {
      return {
        name: "Stream name",
        value: "8",
      }
    }
  }
};

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
  inheritResolversFromInterfaces: true
});

const server = new ApolloServer({ schema });

server.listen().then(({ url }) => {
  console.log(`馃殌 Server ready at ${url}`)
});

Query

{
  content {
    resource
    name
  }
  stream{
    resource
    name
  }
}

Response

{
  "data": {
    "content": {
      "resource": "Content",
      "name": "Some content"
    },
    "stream": {
      "resource": "StreamContent",
      "name": "Stream name"
    }
  }
}

Does this look like it'd cover your use case? If so, I think some documentation here could go a long way. Particularly on our documentation for Unions and Interfaces.

All 3 comments

It's possible to inherit resolvers from an interface, but the more advanced use currently necessitates the use of makeExecutableSchema, which is also exported from apollo-server (and its variants). The key is to use inheritResolversFromInterfaces: true on makeExecutableSchema's options and pass the resulting schema to Apollo Server in place of typeDefs and resolvers.

I've mocked this up quickly in this Glitch, but here's the code, request and result:

import { ApolloServer, gql, makeExecutableSchema } from 'apollo-server';

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  interface IStat {
      resource: String!
      name: String!
  }

  type Content implements IStat {
      resource: String!
      name: String!
      value: String!
  }

  type StreamContent implements IStat {
      resource: String!
      name: String!
      value: String!
      encoding: String!
  }

  type Query {
    content: Content
    stream: StreamContent
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  IStat: {
    resource(parent, args, context, { parentType }) {
      return parentType.name; // (e.g. "Content", "StreamContent", etc.)
    }
  },
  Query: {
    content(parent, args, context, { returnType }) {
      return {
        name: "Some content",
        value: "6",
      };  
    },
    stream() {
      return {
        name: "Stream name",
        value: "8",
      }
    }
  }
};

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
  inheritResolversFromInterfaces: true
});

const server = new ApolloServer({ schema });

server.listen().then(({ url }) => {
  console.log(`馃殌 Server ready at ${url}`)
});

Query

{
  content {
    resource
    name
  }
  stream{
    resource
    name
  }
}

Response

{
  "data": {
    "content": {
      "resource": "Content",
      "name": "Some content"
    },
    "stream": {
      "resource": "StreamContent",
      "name": "Stream name"
    }
  }
}

Does this look like it'd cover your use case? If so, I think some documentation here could go a long way. Particularly on our documentation for Unions and Interfaces.

I'll close this as there hasn't been any additional follow-up in quite some time. Hopefully the information I've previously provided above would be sufficient for anyone else who arrives at this issue in the future.

If anyone is still having problems with this, feel free to ask in the Apollo community on Spectrum.chat, where questions are better suited, in general.

Thanks!

The inheritResolversFromInterfaces: true works well and we should improve the documentation to add something about.

But ... I think the feature request is still relevant, because it could allow this feature without having to use makeExecutableSchema, which add some complexity

Was this page helpful?
0 / 5 - 0 ratings