Skip to content
This repository has been archived by the owner on May 26, 2023. It is now read-only.

How to access additional data across federated services #1

Open
MoonTahoe opened this issue Jun 1, 2019 · 10 comments
Open

How to access additional data across federated services #1

MoonTahoe opened this issue Jun 1, 2019 · 10 comments

Comments

@MoonTahoe
Copy link

Federation is great! I love it! This demo is also awesome, thanks! I do have one question about obtaining data from external services.

Let's say I work on the Accounts team. We're using Apollo Federation and have teams working on independent federated services. All of our teams are totally independent, we cannot request features from other teams. If we want to implement something, we have to do it on our own.

Our customers want a running total of the products that they have purchased. We have saved a list of upc codes locally for each purchase that a user has made. A user account record has the following data in our database:

{
  "name": "Jonathan Toews",
  "productsPurchased": [1, 2, 3, 2, 1]
}

So we are going to add a field that will resolve to the total dollars that a users has spent on all of the products:

type User @key(fields: "id") {
  id: ID!
  name: String
  username: String
  totalDollarsSpent: Float
}

However, when I get to the resolver I do not have enough information about the products that the user has purchased. I only have their UPC codes:

totalDollarsSpent: (user) =>
  user.productsPurchased
    .map(upc => /*Here I also need the price, but I don't have it*/)
    .reduce((total,product) => total + product.price, 0);
  • What would be the best solution for completing this resolver?
  • Is this a use case for federation?
  • Can this be handed through shared context?
  • -or- Would you just send a GraphQL request to the Product service for prices from the resolver?

I'm not sure this is even a case for federation, but I was thinking about all of the use cases for teams who work in complete isolation. Federation gracefully handles every other use case that came up, so I though this may be in the ballpark.

@jbaxleyiii
Copy link
Contributor

jbaxleyiii commented Jun 6, 2019

@MoonTahoe (👋 Alex!) This is a really great question 👍

The tldr; is this isn't supported right now 😭

The exciting answer is this is on the roadmap 🎉 This is what it would look like:

type User @key(fields: "id") {
  id: ID!
  name: String
  username: String
  productsPurchased: [Product] @internal
  totalDollarsSpent: Float @requires(fields: "productsPurchased { price }")
}

extend type Product @key(fields: "upc") {
  upc: String! @external
  price: Int @external
}

A couple things introduced here (that will come in multiple releases):

@internal directive. This will probably come first in the scheme of things. Internal will allow for hiding fields from the overall schema that the client can consume.

@requires support when used on a base type. This will come a little bit later because it complicates the query planner a good bit 😆. Ultimately the query plan would be something like this:

{
  me {
    totalDollarsSpent
  }
}

{
  me {
    id
    productsPurchased {
      upc
    }
  }
}

{
  ... on Product {
    price
  }
}

{
   ... on User {
     totalDollarsSpent
   }
}
{
  totalDollarsSpent: ({ productsPurchased }) =>
    productsPurchased.reduce(({ price }, prev) => price + prev, 0)
}

@MoonTahoe
Copy link
Author

That is really cool. This is sort of how I was trying to use ‘@requires’ at first.

JacksonKearl pushed a commit that referenced this issue Jul 23, 2019
@monoguerin
Copy link

any updates in this?, looks like a feature that we will like to use in my team :)

@jhampton
Copy link

jhampton commented May 2, 2020

@MoonTahoe Thank you for bringing this to my attention. This is in the same vein as https://github.com/apollographql/apollo-server/issues/3621 and other concepts like "@scope", if I'm reading this thread correctly.

I wonder late into the evenings how far GraphQL's concerns should ultimately extend to all of these cases: perhaps it's the expressive, declarative, type-safe nature of GraphQL that causes us all to "lean in" to applying GraphQL to all patterns instead of insisting that its properties (expressive, declarative, type-safe) exist in other systems. Swagger/OAS helped to make great strides in REST, and these are all "echoes" of SOAP, CORBA, and similar attempts at ensuring that systems can be statically-analyzed, descriptive, and discoverable. JSON API and oData have some of these characteristics as well as an API layer. Traditional RDBMS's have always strongly-typed for their internal reasons and efforts to bring this into the ORM layer never really connected all of the dots (unless I'm missing a key implementation somewhere).

With all of this context, I'm going to add this to my list of considerations as we discuss the next steps on this kind of "scoping" of a single graph.

@MoonTahoe
Copy link
Author

Thanks for looking into this @jhampton. Right now the primary solution for this requirement is to send a graphql-request from the resolver, which feels a little like stitching. I like the above solution that @jbaxleyiii presented. It looks like a sweet solution to merge that request in with the query plan as opposed to making an additional graphql-request from within a resolver.

@wzalazar
Copy link

I have the same problem, but with a Mutation that requires some extra data for checking some stuff, I tried to find some pretty solutions in the blog and the documentation, but I didn't find anything so far. In this case, we will decide to use graphql-request pointing to another graphql service. The question will be:

  • How would Apollo federation work with the mutations?
  • Are we missing something?

I will glad if someone Can add some inputs or experiences how we must resolve these situations

@apro23
Copy link

apro23 commented Mar 31, 2021

Hello sir @jbaxleyiii is there an update on this one? thank you

@saravind-hotstar
Copy link

Please do if possible share the best practice in current spec to access additional data across services.

@filwaline
Copy link

filwaline commented Jan 9, 2023

If you're interested in how Apollo Federation can handle cross-service mutations, check out this repository Apollo-Federation-Mutation-Demo. It provides a practical example of accessing external data in a seamless way.

@dudutou
Copy link

dudutou commented Jan 22, 2023

It seems that the requires directive has been added and this issue has been fixed.
https://www.apollographql.com/docs/federation/federated-types/federated-directives#requires

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants