Add more details to static errors raised after the query run
Is your feature request related to a problem? Please describe.
Static errors that makes the query invalid points straight to the error location, i.e.:
{
"errors": [
{
"message": "Field 'documents' is missing required arguments: kind",
"locations": [
{
"line": 7,
"column": 7
}
],
"path": [
"query",
"employees",
"documents",
"kind"
]
}
]
}
But static errors such as non-nullable field does not, i.e.:
{
"errors": [
{
"message": "Cannot return null for non-nullable field Employee.status"
}
]
}
The problem is that it makes really hard to debug since you can have a query that return multiple nodes such as:
query {
employees {
id
fullName
status
}
}
If you have 100 employees, which one did return an empty status?
Describe the solution you'd like
Would be nice to have the error wrapped pointing to the specific object that failed the validation, i.e.:
{
"errors": [
{
"message": "Cannot return null for non-nullable field Employee.status",
"object": {
"wrapping_type": "EmployeeType",
"type": "Employee",
"gid": "..."
}
}
]
}
Describe alternatives you've considered
We built a GraphQL::Schema::FieldExtension to add this context to unhandled exceptions but it doesn't trigger for static errors.
Be aware of the two kinds of static errors: on the query (missing argument, undefined field) and on the result (non-nullable fields).
While the first one doesn't have a backtrace, it does show the line and column on the query and the path.
The second one doesn't have a backtrace and points to the type/field that failed but not the object or a backtrace.
Could be interesting to have the backtrace as well like:
{
"errors": [
{
"message": "Cannot return null for non-nullable field Employee.status",
"object": {
"wrapping_type": "EmployeeType",
"type": "Employee",
"gid": "..."
},
"location": ["/app/types/employee_type.rb", 15]
}
]
}
Probably the location is not trackable in all scenarios like when the object is a Hash, but we could have something designed for the other kinds where the field is not implemented on the type:
{
"errors": [
{
"message": "Cannot return null for non-nullable field Employee.status",
"object": {
"wrapping_type": "EmployeeType",
"type": "Hash",
"value": { "id": "...", "status": "" }
},
"location": null
}
]
}
Additional context
While this extremely useful for debugging, it might leak important data if misused and rendered to the user.
What I'm thinking of is that those information would be added to data.query.static_errors and data.context.errors or conditionally rendered if the Rails.env is development or test.
Alternatively, it could be a feature flag or a use GraphQL::DetailedStaticErrors unless Rails.env.production?.
Hey @sobrinho, we can probably improve this along those lines, or at least support a setup like you're suggesting. Here's where those errors are created:
https://github.com/rmosolgo/graphql-ruby/blob/a5451abb07b86b6ba03b6ae678209d8baa529e68/lib/graphql/execution/interpreter/runtime.rb#L475-L477
A couple options right off the bat:
- Implement
def self.type_error(err, ctx)to identifyInvalidNullErrors and handle them by raising aGraphQL::ExecutionErrorwith the extra context you want. (You could create a subclass ofGraphQL::ExecutionErrorand implementto_hto include whatever metadata you need.) You could usectx[:current_path]to get the runtime path. - Improve
InvalidNullError.new(...)to also receive the parent object (eg theEmployeein your example) so that you can produce better debugging outputs. (You could get the parent object in that code withselection_result.graphql_application_value.)
I'll try to take a look at these soon but I thought I'd go ahead and mention them in case you want to give either one a try.
Coincidentally this is related to https://github.com/rmosolgo/graphql-ruby/issues/5239
As noted in #5239, there is some nuance, but sounds like InvalidNullError could behave more like ExecutionError. Might be able to accept ast_node and path here such that the information is available for to_h as part of the output.