Contributing Fields from Multiple Subgraphs

I’m trying to compute a field’s value from an entity defined in external subgraph, and unable to get the correct output. In the example I have chosen to share I have Shipping and Profile subgraphs. In Shipping I’m declaring the following calculated field:

Schema:

type Shipping @key(fields: "id") {
  ...
  user: User # Profile subgraph
  deliveryInstructions: String @requires(fields: "user { address { street1 stateCode } }")
}

Profile declares User as entity with a __resolveReference function. I have a demo app demonstrating that. As of now, I see the query plan include the correct services in the execution plan but misses to execute the Shipping.deliveryIntructions resolver. What else could I be missing (or would the resolver mapper need) to reach the final step of this query:

Query:

query Product($shippingInformationId: ID!) {
  shippingInformation(id: $shippingInformationId) {
    quantity
    deliveryInstructions
  }
}

Query Plan:

QueryPlan {
  Sequence {
    Fetch(service: "shipping") {
      {
        shippingInformation(id: $shippingInformationId) {
          user {
            __typename
            id
          }
          quantity
        }
      }
    },
    Flatten(path: "shippingInformation.user") {
      Fetch(service: "profile") {
        {
          ... on User {
            __typename
            id
          }
        } =>
        {
          ... on User {
            address {
              street1
              stateCode
            }
          }
        }
      },
    },
    Flatten(path: "shippingInformation") {
      Fetch(service: "shipping") {
        {
          ... on Shipping {
            __typename
            user {
              address {
                street1
                stateCode
              }
            }
            id
          }
        } =>
        {
          ... on Shipping {
            deliveryInstructions
          }
        }
      },
    },
  },
}

Hi @Mitchell_Alderson. Thanks for the feedback. I mentioned in the post the User type is already an entity. Marking it as "external’ should not be necessary, demonstrated by the returned composition error. If you take a look at the resolver map link I provided, you can see the reference resolvers I have in place and the logging statements. When I attempt to execute the query above, I can see the correct invocations except for the deliveryInstructions resolver being called.

Any thoughts?

++ Adding the logs I get when executed the given query. I does all you expect based on the query plan, except for hitting that last field. I have not had the chance to step over the gateway query execution code closely, but it’s either a minor adjustment in the config or the GW not able to reach that node.

[dev:shipping] [shipping-subgraph][Query][shippingInformation] args =>  { id: '1' }
[dev:shipping] [shipping-subgraph][Shipping][user] =>  user-1
[dev:profile] [profile-sub][User][__resolveReference] reference : { __typename: 'User', id: 'user-1' }
[dev:profile] [profile-sub][User][address] root.address:  {
[dev:profile]   street1: '1 Islington Ave',
[dev:profile]   street2: '',
[dev:profile]   city: 'Pennington',
[dev:profile]   stateCode: 'MU',
[dev:profile]   zipCode: '11223'
[dev:profile] }

Hey there @mpiantella,

Using the reference resolver for shipping to return data is what stands out to me (seen here), this needs to be passed through on the type resolver.

Hi @kyleo, do you mind demonstrating what you mean? I’m not sure I follow.

Thanks,
Maria

Mitch was kind enough to create a PR which will result in the behavior as described. We recommend confirming any undesired result with the related dev docs. There are several pieces that need to mesh within each subgraph’s schema, their resolvers, and field directives to contribute them as computed entity fields.

Our existing documentation includes working examples of these pieces. The code for an individual implementation is going to vary, after we plugged in the example here it’s returning the expected data, and wanted to share a code example as a one-time courtesy.

Thanks for the reference back to the links. I started reviewing the PR but I’m short with time today, so I will take in the fix and applying in the coming days.

I got certified recently and walked through the examples you’ve shared. While they closely resemble what I need, they are still contributing to a field that originates on an external graph. Here, I have a computed field defined on an entity that originates in a graph external to the type it requires data from. If that hasn’t come across clearly, I apologize.

Good morning @kyleo. Thank you and @Mitchell_Alderson for the help. As i’m going over the changes I see the solution has been to basically add “Shipping” to Produc subgraph and include the @requires there. As you mentioned above, the docs show this example and it absolutely works. But this is still not what we desire as it would require the Product subgraph to know of type Shipping which is not desired to our data model.

However this doesn’t solve the original scenario. Simple put that would be SubA, SubB and SubC. In SubC type EntityC.calculatedField @requires SubA.EntityA.Field1 and SubB.EntityB.Field2 to yield a result. Where EntityC is only defined in SubC.

@mpiantella Hey just wanted to follow up here to share that we’ve opened up a GitHub issue related to this: Incorrect query plan when @requires starts with local (non-external) fields · Issue #2069 · apollographql/federation · GitHub The workaround Mitch provided should still work as a temporary fix, but we are looking at a long term permanent solution.