-
-
Notifications
You must be signed in to change notification settings - Fork 440
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
Error propagation #531
Comments
The current implementation of |
Thank you so much for your answer 🙏 That’s too bad. We’ve tried to follow the graphql spec as much as possible, and this could be very confusing for external users of our api since the spec specifies that errors should propagate until a parent field is nullable and no further, Errors and Non-Nullability. Would supporting this be a major change? Maybe an extension could solve this? But more importantly maybe, is this a direction you would be interested in going? 😊 |
After I finish the work at hand, I will come to achieve this, and it will be done quickly when I start. |
Released in |
@Fanneyyy I hope you can help me test the new version and give me some feedback. |
Hey, yes of course and thank you so much for this. One thing to note regarding the implementation which can break schemas. type QueryRoot {
nestedErrorTest: ParentObject!
}
type ParentObject {
id: String!
nestedOne: MiddleChild!
}
type MiddleChild {
id: String!
nestedTwo: YoungestChild
}
type YoungestChild {
id: String!
name: String!
} and this query: {
nestedErrorTest {
id
nestedOne {
id
nestedTwo {
id
name <- returns error
}
}
}
} If {
"data": {
"nestedErrorTest": {
"id": "I'm the parent object",
"nestedOne": {
"id": "I'm a middle child",
"nestedTwo": null
}
}
},
"errors": [
{
"message": "YoungestChild failure",
"locations": [
{
"line": 8,
"column": 9
}
],
"path": [
"nestedErrorTest",
"nestedOne",
"nestedTwo",
"name"
]
}
]
} The current implementation however returns the payload below, where {
"data": {
"nestedErrorTest": {
"id": "I'm the parent object",
"nestedOne": {
"id": "I'm a middle child",
"nestedTwo": {
"id": "I'm the youngest child",
"name": null
}
}
}
},
"errors": [
{
"message": "YoungestChild failure",
"locations": [
{
"line": 8,
"column": 9
}
],
"path": [
"nestedErrorTest",
"nestedOne",
"nestedTwo",
"name"
]
}
]
} However, since the error is present this can easily be handled client side. And for most use cases making sure that fields that can return error (be null) are optional will circumvent this issue. |
Thank you for your feedback. I will think about how to propagate null to the next nullable field. 🙂 |
@Fanneyyy I released |
Great, thank you so much 🙏 This looks really good and the schema is always type safe. I.e. when I have an optional value from an external request I would usually create the field like so ( struct ParentObject;
#[Object]
impl ParentObject {
async fn child_opt(&self) -> Option<ChildObject> {
Some(ChildObject)
}
}
struct ChildObject;
#[Object]
impl ChildObject {
async fn opt_description(&self) -> Result<Option<String>> {
let maybe_description = Some("some description".to_owned());
Ok(maybe_description)
}
}
struct QueryRoot;
#[Object]
impl QueryRoot {
async fn parent(&self) -> ParentObject {
ParentObject
}
} This field would be optional in the graph but if it returns an error the error is still propagated to the next optional field in the graph, i.e. the query: {
parent {
childOpt {
optDescription <- returns error
}
}
} Returns: {
"data": {
"parent": {
"childOpt": null
}
},
"errors": [
{
"message": "description error",
"locations": [
{
"line": 4,
"column": 7
}
],
"path": [
"parent",
"childOpt",
"optDescription"
]
}
]
} Even though {
"data": {
"parent": {
"childOpt": {
"optDescription": null
}
}
},
"errors": [
{
"message": "description error",
"locations": [
{
"line": 4,
"column": 7
}
],
"path": [
"parent",
"childOpt",
"optDescription"
]
}
]
} You can however achieve this result by creating the resolver like so: async fn opt_description(&self) -> Option<Result<Option<String>>> {
let _maybe_description = Some("some description".to_owned());
Some(Err("myerror".into()))
} And the response would be correct but the code is a bit confusing 😅 |
@Fanneyyy async fn opt_description(&self) -> Option<Result<String>> {
Some(Err("myerror".into()))
} |
I would prefer If it is not successful it may ONLY be an error. |
@sunli829 My only concern is that both return types, |
@Fanneyyy After Rust supports generic specialization, I will be able to unify the behavior of these two definitions. |
We're working on moving from Juniper to async-graphql and we noticed some issues we're having with error propagation that we didn't have in Juniper.
The issue is that Juniper handles errors so if an error is returned from a non-null field, the null value is propagated up to the first nullable parent field, or the root data object if there are no nullable fields. E.g. the juniper code below still returns data from the
me
query, even though theproduce_error
field returns an error becausesome_obj
is nullable.The same doesn't seem to work for us with async-graphql where the whole query always returns an error if any field does. We can't find any examples in the book or docs so wondering if there's something obvious we're doing wrong?
Basically we're looking for a way for nested fields to return errors without affecting the whole query, is there a good way to do that?
Also, could that solution include a way to return all errors from multiple fields?
Any help would be amazing 🙏
Example from Juniper
Code:
Query:
Response:
Async Graphql with similar approach as with Juniper
Code:
Query:
Response:
The text was updated successfully, but these errors were encountered: