Comments (11)
Being able to define custom error types for production GraphQL usage is critical. In other systems that I maintain, where GraphQL is used as the frontend to all of the backend gRPC microservices, there is a contract defining what our error types will look like for when errors are returned.
According to the GraphQL spec's description of errors, having the message
field present is baseline (and quite logical). Perhaps defining an interface which will allow a higher-level handler in Juniper to take and serialize custom errors according to that interface would be a nice pattern.
from juniper.
Hmm, I think I have an idea on how to implement this if @mhallin doesn't disapprove I'll try to write a PR for it in the next couple of days.
from juniper.
Errors in GraphQL are tricky! I think a clean way of handling failing mutations is one of the less thought-out areas of the whole standard. On the other hand - it might be hard to standardize this on the GraphQL level.
What I've been doing - both in Juniper and when using other systems - is to have the mutation return two fields: a nullable result object and a nullable list of error objects which usually contain a field enum and a message.
This way circumvents the "standard" GraphQL error system for better or worse. An upside is that the errors are actually documented using the same introspective schema like the rest of the system, so you can actually model the error handling pretty rigidly.
That being said, all of these points only cover failing mutations so I'm interested in hearing your proposed solution.
from juniper.
I've been doing some research to ensure my initial idea was a good one. However my idea doesn't agree with the current best practice which is further discussed here. More specifically the best practice appears to be include an error field within your response object. For example, in this issue here it was mentioned that Facebook has something like:
mutation {
signUp(email: "[email protected]", password: "thisbetterbeencrypted") {
# bool
didSucceed,
errors {
# fields
}
}
}
In regards to say failing incorrect user input in a query I would suggest to apply the same thing but the expected data is a nullable object and a nullable error object as well. However, the fact is I haven't found any direct confirmation that this is the case, maybe I should get a key for the Facebook api and have look around, but I digress. Ultimately, by using this approach one can still generally apply the "rules" of presenting an error. These "rules" being:
- If no errors were encountered during the requested operation, the errors entry should not be present in the result.
- If the data entry in the response is null or not present, the errors entry in the response must not be empty. It must contain at least one error. The errors it contains should indicate why no data was able to be returned.
- If the data entry in the response is not null, the errors entry in the response may contain any errors that occurred during execution. If errors occurred during execution, it should contain those errors.
Now this being said it is within the specs to "extend" the error section with our own errors which was my initial idea or at least that is how I interpreted 7.2.2. However, it is noted that at any time the specs could change to include a error that we are using and therefore break any custom errors that were added.
Now that I have read a little bit more into the execution errors it makes sense to treat them as developer only errors and not error that need to be revealed to client.
Anyways, I was hoping to get some input before coding anything up.
from juniper.
My 2 cents...it seems weird to use errors in the response, especially as Relay appears to use the errors entry last I looked at it (facebook/relay#696). Using errors in the response would mean never using onMutationFailure
in that example, which means my client code would be duplicating a (small) part of Relay's error-handling logic for seemingly no gain.
But, as @mhallin points out the introspection benefits of errors in the response are hard to ignore.
Perhaps juniper
can differentiate between ServerError
and ApplicationError
, the former using the error entry and the latter having some nice ergonomics for adding to the payload, optionally also setting the error entry to trigger things like Relay's error handler?
from juniper.
I see... now I am getting why @mhallin said this was tricky! It appears that both ways are encouraged for now as indicated here. So I think the only requirements for errors is that they must contain the message field, and an optional location field which is going to be tricky to enforce but should be possible to do.
from juniper.
I don't really see the big benefit of embedding the errors in the data.
It would be useful if errors were typed, but they are polymorphic, so all I would end up with is
type Error {
code: String!
message: String!
data: JSON! // Custom scalar type...
}
Which doesn't really help much compared to just having them in err.
Errors in the data are also prone to break assumptions, for example: javascript client library that rejects a Promise if err is in the response.
So I'd definitely petition for the possibility of complex errors in err
.
from juniper.
This comment on graphql-js, and this one on the GraphQL spec itself (that TheServerAsterisk linked above) summarize my view on error handling in GraphQL.
It all depends on how you imagine the error handling should work. For e.g. sign up/log in mutations, you want to include all errors, their fields, and a human-readable string that goes with each field. For these cases, I've been using a schema that looks kind of like:
type MutationType {
signUpUser(email: String!, username: String!, password: String!): SignUpResult
}
type SignUpResult {
user: User
errors: [AuthFieldError]
}
type AuthFieldError {
field: AuthField!
message: String!
}
enum AuthField {
USERNAME
PASSWORD
EMAIL
}
Note that this schema does not include data for e.g. database or other internal errors. For those kinds of errors, or for mutations that are expected to always work, I just use the regular GraphQL error handling with a simple message.
What's the benefit of having a schema for the errors? I would argue that it's the same benefit of having a schema for the "regular" data in the first place :) You can have your error views be driven by GraphQL queries just like your other views, reason about types and state just like you would with the "successful" data. I think handling of expected errors should be just as robust as the rest of the application.
All that being said, I'm definitely not opposed to the idea of being able to attach more data to the FieldError
type. I just want to know the intended use case before we start thinking how to implement it.
from juniper.
I dug into the relay code it appears that they are using hasOwnProperty("errors") as a means to check whether the query/mutation was successful or not see here.
Furthermore, it does seem like some real world examples are not using the error field within a GraphQl Object. For example, Github's GraphQL will return a standard execution error with an added type field. Don't have facebook so I can't confirm exactly how it is. That being said I just found a publically facing GraphQl API converted a JSON object into a string and shoved it into the message field. So this isn't a consistent thing between implementations or the individuals developing with such said tool.
However, I don't think the use case is in dispute here because ultimately whether we use the way @mhallin suggested or we allow custom errors through the FieldResult they are both attempting to create client friendly errors (as far I can tell), what is in my opinion at dispute is how errors of this nature are to be treated whether it be part of a "successful" payload or an "erroneous" payload. Even though I agree with the way @mhallin has suggested achieving this task I would still suggest that this should be the end-users choice so long GraphQl spec allow the behavior.
from juniper.
This should be figured out with / before 0.9, since it might require a breaking change.
from juniper.
One possible solution to this and #69 would be to make FieldError
a struct like:
struct FieldError {
message: String,
extra_data: Value,
}
impl<E> From<E> for FieldError where E: Display {
fn from(err: E) -> FieldError {
FieldError { message: format!("{}", err), extra_data: Value::null() }
}
}
impl FieldError {
pub fn new<E: Display>(err: E, extra_data: Value) {
FieldError { message: format!("{}", err), extra_data: extra_data }
}
Then we could append or include the extra data into the error object when serializing it. Now you could do either the following:
let txn = executor.context().db.transaction()?;
// Would result in { "message": "<internal db error>", "locations": [...] }
// or
let txn = executor.context().db.transaction().map_err(
|e| FieldError::new(e, Value::string("Could not start transaction"))?;
// Would result in { "message": "<internal db error>", "locations": [...], "data": "Could not start transaction" }
(With reservations for not having checked that the code actually compiles :)
from juniper.
Related Issues (20)
- string mangling on dynamic schema due to smartstring dependency HOT 5
- Rust 1.67 emitting schema non-utf-8 field names HOT 3
- Generated code fails the `clippy::str_to_string` lint HOT 3
- Interface not working as expected with imlp HOT 3
- Expected query, got subscription. HOT 3
- `juniper_actix` does not compile with the `subscription` feature enabled HOT 2
- Old version of book is still online HOT 1
- operation_name not being set in juniper_hyper library HOT 2
- I'm having issues with App Data configuration
- Looking for Help & Community HOT 2
- `juniper_warp::make_graphql_filter` improperly returns 4xx on some `context_extractor` 5xx errors HOT 5
- Gitter link resolves to 404 HOT 1
- A better way to keep graphiql updated HOT 1
- Example to integrate with Axum HOT 7
- Subscription argumennt Error using juniper_graphql_transport_ws HOT 2
- GraphQLInputObject macro fails if crate has a local type alias named "Result"
- Create new release HOT 7
- expected tuple struct or tuple variant, found function `Ok` HOT 2
- Input type, determine if key set or unset HOT 2
- `#[graphql_object]` hides syntax errors HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from juniper.