-
Notifications
You must be signed in to change notification settings - Fork 161
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
Extensibility point is needed to return 401 or 500 based on request execution #159
Comments
With GraphQL, HTTP should be thought of as just the transport. Using the HTTP status code to communicate application status is a REST concept and not really compatible with GraphQL, especially because you can have a result that is mostly successful but with some errors. So the idea would be that any 4xx or 5xx error is a transport issue. Your application should look for execution errors in the GraphQL response and decide how to handle them. |
Thanks for the response. First of all, status code is not a REST concept; it is HTTP concept. Because of this, REST vs GraphQL holywar is irrelevant here. Speaking about the middleware we are discussing transport-specific code, there is no contradiction. 500 Any HTTP service should be able to communicate that it has failed miserably and cannot provide a valid GraphQL error, example being GraphQL layer throwing an exception. 401 When auth is done with expiring tokens, existing clients rely on API returning 401. I currently have my own GraphQL middleware implementation that currently has just the code I referenced above incompatible with current graphql-dotnet/server implementation. My project makes use of graphql-dotnet/authorization, and things work like a charm. This is why I want the ability to return business-relevant HTTP status codes upstream. UPD: Edited to fix MD numbered list auto numeration error. |
Found this. I think that here should be an extensibility point because old code basically returned 400 for whatever error and new code returns 200 for whatever error. User should be able to decide instead. |
Here's where that change was discussed: #149. 500 errors are already covered. If there is a server configuration issue or an unhandled exception somewhere in the request pipeline, a 500 error will be returned. I have a PR (#158) to add support for endpoint authorization which will return a 401 or 403 error as appropriate. |
Thanks for the useful resources. I followed the links and had a read. The PR #158 looks fine. What I'm speaking about is a different story.
Could you provide an official source of the idea that you need to unconditionally always return 200 even when you provide errors the JSON? Here are examples from officially recommended graphql node.js implementations. express-graphql here return 500 for responses with errors. Here apollo-server actually return 400 or 500 through some hardcoded logic relevant for their own code comparable to the piece of code I have given as an example what the requested extensibility point might be used for. GraphQL layer wraps business and data access logic. Choosing result code for HTTP services response is can be a business decision. There is no GraphQL/transport parallel universes. HTTP layer can contain it's own business logic deciding what code to return based on the results of GraphQL layer work and this is OK. Another point is that there are pieces of automation that send HTTP requests and cannot parse JSON responses. |
All I'm saying is that I don't think the middleware should interpret errors returned from a partially successful GraphQL execution and set an HTTP status code based on that. The example you gave from express-graphql is returning a 500 response if the data from the execution result is null, because it indicates a complete execution failure. I think that is appropriate and should probably be added to this project. The example in the apollo-server is catching an error thrown when creating the user context. That is before the query execution. In this project, if an exception is thrown when creating the user context, the exception will bubble up and result in a 500 error: Neither of these projects set a status code based on the presence of errors in the GraphQL execution result. |
Well, I guess, I can sum up that you do not know any real official sources prescribing returning 200 on errors inside GraphQL no matter what. I just want to point this circumstance clearly because doing so is super-duper opinionated. There is a reason for this: GraphQL is abstracted form the transport layer which means that it does not prescribe anything about it, including returning 200 on errors. Some protocols have result codes, some do not. As for now I didn't see any arguments for the opinionated design decision other than "I think so". My examples (would you provide you own examples?) was about demonstrating that others approach this "always 200" problem differently. First example: is completely valid, returning 500 on magic rule "null is error" is a way to manifest some business requirement and is in fact based on graphql execution result. You arguments so far were:
My arguments thus far are:
|
|
About apollo-server, I'll provide TL;DR of what they actually do.
So, they do support a way to set an arbitrary HTTP status code from inner layers and this is a way of GraphQL execution determining the status code. |
Sorry I don’t have time for a more detailed and thorough response. I never meant to imply that the middleware should always return 200 OK no matter what. Just that the middleware should not be responsible for interpreting errors on the execution result. These other projects are not doing that. I also didn’t mean to give the impression that I think this project is perfect as-is. I was just trying to work out the best solution for what you are trying to accomplish. Thanks for pointing out that the apollo-server project catches I think a similar approach could be easily implemented in this project with the new |
I personally like the idea of throwing something like Another interesting observation, I hoped you could make from my examples that different implementations approach error handling/status codes problem differently from each other and sometimes with rather awkward solutions like checking if result is empty. graphql-dotnet/server used to have the same code changing over time is not a coincidence as well. As I see it, the problem lies in developers not really understanding that they are trying to provide a general solution for something entirely depending on business requirements. For different systems it might be OK respectively:
IMO both somehow extending About doing something that others don't do: I skimmed through 2 major JS and a major Java implementation, truth is that they are not that much more mature, you can quickly understand this from looking on their test suites and issue trackers. So it is OK to do something differently. I'm personally interested in a way of extending control over result codes. Maybe, others have something to say about all this as well. |
As I mentioned, there is a new I like the The apollo-server repository has almost 5k stars and over 500 forks. It definitely seems to be the most widely adopted graphql server, so I don't think taking some inspiration from them is a bad idea. But, I agree, it doesn't mean we can't do something different. |
I noted that Anyway, I need both 500 and other status codes, and not only technical, but also business requirements-related. This can be easily implemented now with 10-20 lines of non-test code and zero risks for graphql-dotnet library. |
Not sure what you're referring to about the I don't see why it would be appropriate to look at unobserved task exceptions in one of the GraphQL class libraries. That should be handled by consuming code. There is one open issue related to unobserved tasks (graphql-dotnet/graphql-dotnet#781), but once that is fixed, it shouldn't really be a concern. Yes, when a So are you going to do a PR for the |
I have nothing against |
It's not that difficult to execute the query by yourself in case you need custom behavior for error handling. Closing this for now. |
I agree, in fact this is what I do. Also nobody else expressed interest in this. @johnrutherford , thanks for the discussion. |
GraphQLHttpMiddleware is getting better, so I would be happy to make use of it in my projects.
If I understand correctly, current implementation only returns anything other than 200 only based on information available before (IGraphQLExecuter).ExecuteAsync call.
The problem is following: sometimes it is only possible to figure out that middleware needs to return a code different from 200 or 400 only during the request execution. Notable examples are 500 and 401.
Concrete examples to illustrate the concept:
https://github.com/graphql-dotnet/examples/blob/master/src/AspNetCoreCustom/Example/GraphQLMiddleware.cs#L74
graphql-dotnet/authorization#1 (comment)
In my opinion, we need an extensibility point roughly here
https://github.com/graphql-dotnet/server/blob/develop/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs#L88
We can go "MVC way" and introduce something like ActionResult, which would later be evaluate by some "executor" resolved as a service.
Of course there is unlimited number of options of how to do this; I will be happy with any solution be it Chain of Responsibility, some analog of IActionFilter/IResultFilter, a delegate parameter or anything else.
I would go with "ActionResult" solution, can provide a PR.
The text was updated successfully, but these errors were encountered: