javascriptnode.jsgraphqlapolloapollo-federation

How to resolve type from another subgraph in a Apollo GraphQL federated schema?


I have an Order subgraph and a Menu subgraph. The order subgraph returns customers orders and the Menu subgraph return information about menu's and the menu items.

When I fetch an order, I want the order data returned from the Order subgraph but then any correlated item information such as price and name etc needs to be resolved from the Menu subgraph.

My schema's are as follows:

// Order subgraph
type Order @key(fields: "id") {
  id: ID!
  item: MenuItem! // MenuItem type exists in the Menu subgraph
}
// Menu subgraph
type MenuItem @key(fields: "id") {
  id: ID!
  name: String!
  price Float!
}

When I start my Order subgraph, I get the error: Error: Unknown type: "MenuItem"..

Sounds like I might be able to use @external or @provides directives in order to tell my subgraph that the types exist in another subgraph but I can't seem to get it to work.

How can I share types across subgraphs and how does the data resolve from one to another?


Solution

  • I have recently learned about Federated Subgraphs and I think I can help you. All of my information comes from the courses they have here (https://www.apollographql.com/tutorials/). If you take a look at the Voyage 1 course, you can learn about entities in detail.

    Since you want MenuItems to be resolved by the Menu subgraph through the Orders subgraph, I'm assuming that data-wise an Order would have an ID for a MenuItem. You wouldn't need the @provides or @external directives in this case. What you could do, is this:

    In your Order subgraph

    type Order @key(fields: "id") {
      id: ID!
      item: MenuItem! // MenuItem type exists in the Menu subgraph
    }
    
    type MenuItem @key(fields: "id", resolvable: false) {
       id: ID!
    }
    
    function orderById(orderId) {
       // ...content...
       returns {
          // ...order information...
          menuItem: {
             id: menuItemID
          }
       }
    }
    
    // Order resolvers
    const resolvers = {
       Query: {
          orderById: ...
       }
       Order: {
          menuItem: ({menuItem}: Order) => {
             return {id: menuItem.id}
          }
       }
    }
    

    In the Menu subgraph

    type MenuItem @key(fields: "id") @shareable {
      id: ID!
      name: String!
      price Float!
    }
    
    // Menu resolvers
    const resolvers = {
       Query: {
          menuItemByID: ...
       }
       MenuItem: {
          __resolveReference: ({id}: MenuItem,{dataSources}: ServerContext) => {
             return dataSources.menuItemAPI.menuItemByID(id)
          }
       }
    }
    

    What happens now, is that whenever the Router sees a request for an Order, you ask to return a menuItem within that Order, it will know to call the Menu subgraph to resolve menuItem. Hope this helps, let me know if you have any questions