New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[apollo-gateway] Question about nullability and cross-service joins #860
Comments
👍 On this request. Here's an example of this problem based on https://github.com/apollographql/federation-demo Steps to reproduce:
{
topProducts {
name
inStock
}
} I believe that having Another concerning aspect is the error message:
It leaks sensitive details like the GQL endpoint that it supposed to abstract. Thanks for your consideration. |
A few follow-up thoughts: My take here is that the correct behavior is to ensure (in the DOWNSTREAM SERVICE), that responses respect nullability, and that the types in question can optionally return a If the user experience expects a given set of child objects to be available and they’re not, a reasonable pattern would be to hide the related UI. But if the type is marked non-null, the right answer might be to contact support (because there must be something there in order to complete a task or flow.)
So as a graph administrator, I can apply these guidelines, make it a part of schema/code review, and write tests for it. Thank you for the callout @jodosha on the leaking of error details. This could be reformatted using the existing Apollo Server hooks: https://github.com/apollographql/apollo-server/blob/570f548b88750a06fbf5f67a4abe78fb0f870ccd/docs/source/data/errors.md#masking-and-logging-errors I'd like to close this out as an active issue, but please feel free to re-open if this surfaces with better or more-specific patterns and practices from the field. Thank you! |
@jhampton Thanks for the pointer to masking errors, but still there is a bit that is missing IMO: the correlation between the error and the field, ideally with If you look again at the output, there is no hint for the caller that From the caller perspective I don't know if I assume that the query planner can associate fields (like EDIT: Another option would be to add a callback to class MyGraphQLDatasource extends RemoteGraphQLDataSource {
didReceiveError({ error, response, request, context }) {
}
}
const gateway = new ApolloGateway({
buildService({ name, url }) {
return new MyGraphQLDatasource({ url });
},
}); Ideally |
I am also concerned about the way gateway handles partial responses if one of the underlying subgraph services are unable to respond. I think it should be a way:
This will make a whole federated gateway way more fault tolerant. |
Thanks @kindermax will take a look 👍 cc @martijnwalraven @pcmanus @abernix - something to consider for the next round of Federation changes |
Throwing my two cents in here. I would agree that forcing Without breaking existing GQL spec expectations with regards to nullable vs. non-nullable fields, I wonder if something like a
There is also a potential option to make this sort of functionality default in federated implementations, avoiding the use of a directive attribution unless you want to opt out of this sort of functionality... something like a Option 1 Exampleextend type User @key(fields: "id") {
id: ID! @external
reviews: ReviewConnection! @faultTolerant
}
type ReviewConnection {
edges: [ReviewEdge!]!
nodes: [Review!]!
pageInfo: PageInfo!
} would compile to the following in the supergraph schema and guarantee a result set that clients explicitly detect and have to handle extend type User @key(fields: "id") {
id: ID! @external
reviews: UserReviewsFaultTolerantPayload!
}
union UserReviewsFaultTolerantPayload = ReviewConnection | ServiceOutageError
type ReviewConnection {
edges: [ReviewEdge!]!
nodes: [Review!]!
pageInfo: PageInfo!
}
# I'm sure we can come up with something better here :)
type ServiceOutageError {
serviceName: String!
statusCode: Int!
message: String!
} Option 2 Exampleextend type User @key(fields: "id") {
id: ID! @external
reviews: ReviewConnection! @faultTolerant
}
type ReviewConnection {
edges: [ReviewEdge!]!
nodes: [Review!]!
pageInfo: PageInfo!
} would simply compile to the following in the supergraph schema, where again, the extend type User @key(fields: "id") {
id: ID! @external
reviews: ReviewConnection
}
type ReviewConnection {
edges: [ReviewEdge!]!
nodes: [Review!]!
pageInfo: PageInfo!
} |
The more I think about what I wrote above, the less I like it. Option 1 above actually feels like it starts to leak the internals of federation into our frontend by explicitly defining where services extend other services. I can't decided if this is a necessarily evil or not. Ensuring the gateway is returning sensible errors feels like a reasonable way to handle this, but either way, knowing that there was a service outage and handling that in our frontend doesn't feel much different. It will be interesting to see what comes of graphql/graphql-spec#867 and if that plays a role in how we think about this. |
I'm not sure why this is closed but I'd like to add that the problem is present even when all subgraphs are up and running as expected. Please read my explanation here: apollographql/router#1308 (comment) |
Hi @trevor-scheer, @jhampton and friends,
Any time the query planner calls another service, there's an additional chance of failure. Consider the following two-service federated graph:
Because
User.reviews
is a non-nullable ReviewConnection, if the request to the Reviews service fails (due to a network partition or service downtime) the response will fail validation and we won't get any data at all.What are your thoughts on making
User.reviews
nullable to better support partial data? It's kind of a shame to leak implementation details into the schema. Are y'all documenting best practices like this anywhere?Would we possibly want a schema linter that enforces that cross-service data is nullable to allow partial data?
Alternatively, would it be possible to handle failures like this?
I have no idea how you'd do this generically, but maintaining the contract by returning empty data plus an error would be very cool.
The text was updated successfully, but these errors were encountered: