GraphQL::Client::ImplicitlyFetchedFieldError
Similar to UnfetchedFieldError, but raised when trying to access a field on a GraphQL
response type that happens to be fetched elsewhere by another fragment but not by the current fragment. The data is available, but isn't safe to rely on until it is explicitly added to the fragment.
This protection is similar to Relay's Data Masking feature.
Parent Data Leak
One source of these data leaks may come from a parent fragment fetching the data used down in a nested subview.
For instance, a controller may fetch a user and include its fullName
.
UserQuery = Client.parse <<-'GRAPHQL'
query {
user(name: "Josh") {
fullName
#...Views::Users::Show::User
}
}
GRAPHQL
Many layers deep, a contact info helper might also too want to make use of the user's fullName
.
UserFragment = Client.parse <<-'GRAPHQL'
fragment on User {
location
# forgot fullName
}
GRAPHQL
user = UserFragment.new(user)
# ok as `location` was explicitly queried
user.location
# raises UnfetchedFieldError, missing fullName field in query
user.full_name
In this case, the raw GraphQL will include both location
and fullName
:
{
"user": {
"fullName": "Joshua Peek",
"location": "Chicago"
}
}
If the controller for some reason decides its doesn't care about fullName
anymore and stops querying it, it will break the helper. The developer just looking at that controller file isn't going to know some other helper on the other side of the codebase still cares about fullName
.
Self contained functions should only safely rely on data dependencies they explicitly ask for. If both the controller and our helper explicitly state they both need fullName
, that data will always be fetched even if the data requirements for one of the functions changes.
Child Data Leak
Similar to the parent data leak scenario, but occurs when a subview fetches data that our root view didn't explicitly ask for.
<%graphql
fragment User on User {
fullName
location
}
%>
UserQuery = Client.parse <<-'GRAPHQL'
query {
user(name: "Josh") {
#...Views::Users::Show::User
}
}
GRAPHQL
user = UserQuery.new(data)
# raises UnfetchedFieldError, missing fullName field in query
user.full_name
The raw flattened data will include fullName
just like the previous example. But again, we should depend on our UserQuery
always having fullName
available show the subview be modified.