3

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?

Stretch0
  • 8,362
  • 13
  • 71
  • 133

1 Answers1

1

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

  • Start by declaring a MenuItem entity 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!
}
  • Next, we move onto your Order resolver. For this example, since you didnt outline, I'll just say you have an orderById function you use to fetch an order. In that function, you want to ensure that an order returns the ID of a menu item somewhere. For example:
function orderById(orderId) {
   // ...content...
   returns {
      // ...order information...
      menuItem: {
         id: menuItemID
      }
   }
}
  • This next step is an important bit to making this work. We need to tell the router to pass the ID of a menu item down to the Menu subgraph when we call for an Order. To do this we create a resolve for our menuItem field in our ResolversMap (or where you outline how things resolve with what functions). You can do that like so:
// Order resolvers
const resolvers = {
   Query: {
      orderById: ...
   }
   Order: {
      menuItem: ({menuItem}: Order) => {
         return {id: menuItem.id}
      }
   }
}

In the Menu subgraph

  • I would start by making Menu a sharable entity.
type MenuItem @key(fields: "id") @shareable {
  id: ID!
  name: String!
  price Float!
}
  • This is the last important bit to making this work. When we set up our Order resolver to pass that ID down it has to be received by the Menu subgraph. To do this, we create a __resolveReference. Here I am also assuming that you have setup some sort of API for retrieving a menu item from you database. Under the Menu subgraph resolvers, go ahead and add this:
// 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

Collecto
  • 89
  • 9