juniper
juniper copied to clipboard
GraphQL specification violation in how Juniper handled field error propagation inside of fragment spreads
Describe the bug I believe Juniper isn't spec compliant in how it's handling field error propagation. Relevant excerpt from the GraphQL spec about how to handle field errors on non-nullable fields.
Since Non-Null type fields cannot be null, field errors are propagated to be handled by the parent field. If the parent field may be null then it resolves to null, otherwise if it is a Non-Null type, the field error is further propagated to its parent field.
Juniper follows this behavior when the query doesn't have any fragments, but if the field error only occurs in a fragment, "null" won't propagate to the closest nullable ancestor field, but instead seems to just make the fields in the fragment null, while not affecting fields outside of the fragment.
My interpretation of the GraphQL spec is that the way a query is partitioned into fragments shouldn't affect the query response. I'm not completely sure of this, but I also can't find any mention of how fragments should change the way field errors are handled.
To Reproduce Unit test demonstrating divergence in behavior
use juniper::{graphql_object, EmptyMutation, EmptySubscription, Variables};
struct MyObject;
#[graphql_object]
impl MyObject {
fn erroring_field() -> Result<i32, &'static str> {
Err("This field always errors")
}
}
// Arbitrary context data.
struct Ctx;
impl juniper::Context for Ctx {}
struct Query;
#[graphql_object]
#[graphql(context = Ctx)]
impl Query {
fn my_object() -> MyObject {
MyObject {}
}
fn just_a_field() -> i32 {
3
}
}
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;
fn main() {
let execute_query = |query: &str| {
let ctx = Ctx;
juniper::execute_sync(
query,
None,
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
&Variables::new(),
&ctx,
)
.unwrap()
};
let (res, _) = execute_query("{ myObject { erroringField } justAField }");
let (res_fragment, _) = execute_query(
"query { myObject { ...MyObjectFragment } justAField } fragment MyObjectFragment on MyObject { erroringField }",
);
assert_eq!(res, res_fragment);
}
Expected behavior Expect field errors that occur inside fragments to propagate nullability to their closest nullable ancestor, even if that ancestor is outside of the fragment itself. equivalently: Expect above code to not panic.
Additional context N/A