Good afternoon!
In my project, I created a service that has an interface called products.
And also created several types that implement the products interface.
The project also has two services. Organization and orders.
The product service resolves without problems in the service of organizations, query works in both directions (Organization - products, products - organization).
But when trying to resolve the product service in the order service, problems arise.
Apollo Playground throws an error
Abstract type "Product" must resolve to an Object type at runtime for field "Position.product". Either the "Product" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function."
resolveType already has in the products service and works correctly.
Can you please tell me if there is an example of how to solve such problems with interfaces?
Organization Serv:
const typeDefs = gql`
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0",
import: ["@key", "@shareable", "@external"])
type Organization @key(fields: "id") {
id: Int
name: String
}
type Query {
organizations: [Organization]
organization (id: Int): Organization
}
`;
const resolvers = {
Organization: {
__resolveReference(ref) {
console.log(ref);
return orgList.find((org) => org.id === ref.id);
},
},
Query: {
organizations(parent, args, context, info) {
return orgList;
},
organization(parent, args, context, info) {
return orgList.find((org) => org.id === args.id);
},
},
};
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
server.listen(4001).then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Product serv
const typeDefs = gql`
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0",
import: ["@key", "@shareable", "@external"])
interface Product {
id: Int
name: String
type: String
customer: Organization
}
type Spring implements Product @shareable @key(fields: "id"){
id: Int
name: String
type: String
customer: Organization
d: Float
}
type Tool implements Product @shareable @key(fields: "id"){
id: Int
name: String
type: String
customer: Organization
h: Int
}
extend type Organization @key(fields: "id"){
id: Int @external
products: [Product]
}
type Query {
products: [Product]
}
`;
const resolvers = {
Product: {
__resolveType(product, context, info) {
if (product.type == "spring") {
return "Spring";
}
if (product.type == "tool") {
return "Tool";
}
return null; // GraphQLError is thrown
},
customer(product) {
return {
__typename: "Organization",
id: product.id,
};
},
},
Spring: {
__resolveReference(ref) {
return products.find((product) => product.id === ref.id);
},
customer(product) {
return {
__typename: "Organization",
id: product.id,
};
},
},
Tool: {
__resolveReference(ref) {
return products.find((product) => product.id === ref.id);
},
customer(product) {
return {
__typename: "Organization",
id: product.id,
};
},
},
Organization: {
products: (organization) => {
return products.filter((spring) => spring.customer === organization.id);
},
},
Query: {
products() {
return products;
},
},
};
And order serv. Everything works correctly in the order service, until the attempt to resolve the interface
In this service, the resolvetype is inserted almost everywhere (it was a panic), but it did not help anywhere.
const typeDefs = gql`
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0",
import: ["@key", "@shareable", "@external"])
type Order @key(fields: "id"){
id: Int
customer: Organization
postions: [Position]
}
type Position @shareable @key(fields: "id"){
id: Int
quantity: Int
product: Product
}
extend type Organization @key(fields: "id"){
id: Int
orders: [Order]
}
interface Product {
id: Int
name: String
type: String
customer: Organization
}
type Tool implements Product @shareable @key(fields: "id"){
id: Int
name: String
type: String
customer: Organization
}
type Spring implements Product @shareable @key(fields: "id"){
id: Int
name: String
type: String
customer: Organization
}
type Query{
orders: [Order]
}
`;
const resolvers = {
Product: {
__resolveType(product, context, info) {
if (product.type == "spring") {
return "Spring";
}
if (product.type == "tool") {
return "Tool";
}
return null; // GraphQLError is thrown
},
},
Order: {
__resolveReference(ref) {
return orderList.find((order) => order.id === ref.id);
},
customer(order) {
return {
__typename: "Organization",
id: order.id,
};
},
postions(order) {
let data = postionList.filter((position) => position.id === order.id);
return data;
},
},
Position: {
__resolveReference(ref) {
return postionList.filter((position) => position.id === ref.id);
},
product: {
__resolveType(product, context, info) {
if (product.type == "spring") {
return "Spring";
}
if (product.type == "tool") {
return "Tool";
}
return null; // GraphQLError is thrown
},
},
},
Organization: {
orders: (organization) => {
let data = orderList.filter((order) => {
return order.customer === organization.id;
});
return data;
},
},
Query: {
orders(parent, args, context, info) {
return orderList;
},
},
};
UPD 1
- Interfaces don’t support keys, I know it.
- If you define the product field in positions via __typename: “Product” and remove __resolveType from the products of the orders service, then the error “Abstract type "Product" was resolved to a non-object type "Product".”,
const resolvers = {
// Product: {
// __resolveType(product, context, info) {
// console.log("Хыху");
// if (product.type == "spring") {
// return "Spring";
// }
// if (product.type == "tool") {
// return "Tool";
// }
// return null; // GraphQLError is thrown
// },
// },
Order: {
__resolveReference(ref) {
return orderList.find((order) => order.id === ref.id);
},
customer(order) {
return {
__typename: "Organization",
id: order.id,
};
},
postions(order) {
let data = postionList.filter((position) => position.id === order.id);
return data;
},
},
Position: {
__resolveReference(ref) {
return postionList.filter((position) => position.id === ref.id);
},
product(product) {
return {
__typename: "Product",
id: product.id,
};
},
},