GraphQL with status code is correct solution? [duplicate] - graphql

This question already has answers here:
GraphQL - How to respond with different status code?
(5 answers)
Closed 3 years ago.
Previous When I made API server with RestAPI, I return data with HTTP status code.
So, Frontend receive status code from server, it determined request is success of fail.
I know that graphQL has error fields, and can refer it to solve this issue.
But I want to change response status code that send to client.
This way is correct and stable way?
Or, When use graphQL, do not change status code and just determine by error field is standard way?
Any suggestions would be appreciated :)
Thanks.

[...] do not change status code and just determine by error field is standard way?
YES do not manage errors using status codes, they are Http related and GraphQL aim at being protocol/framework agnostic so that everything you need should be inside your output.
As you said there can be an errors field in your response:
The errors entry in the response is a non‐empty list of errors, where each error is a map.
If no errors were encountered during the requested operation, the errors entry should not be present in the result.
The spec states that errors field entries can have a field named extensions:
GraphQL services may provide an additional entry to errors with key extensions. This entry, if set, must have a map as its value. This entry is reserved for implementors to add additional information to errors however they see fit, and there are no additional restrictions on its contents.
Using the extensions field you can add custom machine-readable information to your errors like the key code here.
{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [ { "line": 6, "column": 7 } ],
"path": [ "hero", "heroFriends", 1, "name" ],
"extensions": {
"code": "CAN_NOT_FETCH_BY_ID",
"timestamp": "Fri Feb 9 14:33:09 UTC 2018"
}
}
]
}
Apollo Prophecy
To make error management easier I created a codegen CLI that generate throwable errors classes for the server and facilitate error handling for client.
https://github.com/theGlenn/apollo-prophecy

Related

What is the RegionsVersion property used with the Google Play Developer API?

I am trying to use the Google Play Developer API to patch a subscription using the REST endpoint.
https://developers.google.com/android-publisher/api-ref/rest/v3/monetization.subscriptions/patch
The required query parameters are updatedMask and regionsVersion. When I click on the RegionsVersion for documentation it just says:
A string representing version of the available regions being used for
the specified resource.
https://developers.google.com/android-publisher/api-ref/rest/v3/RegionsVersion
Based on that description I'm not sure what kind of value it's expecting other than an object with a version property that is a string. When I omit the regionsVersion parameter is returns the following error:
{
"error": {
"code": 400,
"message": "Regions version should be set to the default value 2022/01.",
"status": "INVALID_ARGUMENT"
}
}
Does anyone have any idea or an example of how this required parameter is intended to be used?
I had the same issue and I found that since it's s JSON object you'll have to set its property like that with query params:
?updateMask=listings&regionsVersion.version=2022/01
This worked fine for me!

What cx code must be used in the Programmable Search Engine JSON Api endpoint

According to the rest api documentation of the Programmable Search Engine documentation, the cx parameter must contain the Programmable Search Engine ID. The example uses 017576662512468239146:omuauf_lfve as example. When making our own search engine on https://cse.google.com/, it shows a hexadecimal search engine id, but when using that as the input for the cx parameter we get a bad request error. Using our API key with the example search engine code does work, which makes me believe that we are just entering something invalid into this field. I am unsure however where we are supposed to get the information for this field otherwise. What cx code must be used in the Programmable Search Engine JSON api endpoint?
{
"error": {
"code": 400,
"message": "Request contains an invalid argument.",
"errors": [
{
"message": "Request contains an invalid argument.",
"domain": "global",
"reason": "badRequest"
}
],
"status": "INVALID_ARGUMENT"
}
}
This was likely an issue with Google's api itself not accepting the new format, as the documentation now mentions
Note: The Search Engine ID (cx) can be of different format (e.g. 8ac1ab64606d234f1)

Issue uploading files using Graphql / Altair Client

I'm using the Altair Client to test some file uploads via Graphql. If I write out my query, and leave the variables blank, I get back an error response
Mutation:
mutation UploadCSV($fileData: Upload!) {
uploadCsv(fileData: $fileData) {
message
errors
successCount
}
}
Errors:
"message": "In argument \"fileData\": Expected type \"Upload!\", found null."
"message": "Variable \"fileData\": Expected non-null, found null."
This is exactly what I'd expect, because I haven't attached a file yet. So I follow the Altair instructions and pick a file, and give it the name fileData. Now when I click Send Request, I get the error "message": "No query document supplied", which is the error you get when no query/mutation has been sent. What am I doing wrong here?

HTTP status code handling in GraphQL APIs

A lot of resources say, that GraphQL should always respond with a 200 status code, even when an error occurred:
https://www.graph.cool/docs/faq/api-eep0ugh1wa/#how-does-error-handling-work-with-graphcool
https://github.com/rmosolgo/graphql-ruby/issues/1130#issuecomment-347373937
https://blog.hasura.io/handling-graphql-hasura-errors-with-react/
Because GraphQL can return multiple responses in one response, this makes sense. When a user requests two resources in one request, and only has access to the first resource, you can send back the first resource and return a forbidden error for the second resource.
However, this is just something I figured out along the way reading docs of multiple GraphQL libraries and blog posts. I didn't find anything about HTTP status codes in the offical specs, here https://spec.graphql.org/ or here https://graphql.org/
So I still have a few questions left:
Is it ok to return a HTTP 500 status code if I have an unexpected server error?
Is it ok to return a HTTP 401 status code, if credentials are wrong?
Should I include the potential HTTP status code inside the errors key of the GraphQL response like this
{
"errors" => [{
"message" => "Graphql::Forbidden",
"locations" => [],
"extensions" => {
"error_class" => "Graphql::Forbidden", "status" => 403
}
}]
}
Should I match common errors like a wrong field name to the HTTP status code 400 Bad Request?
{
"errors" => [{
"message" => "Field 'foobar' doesn't exist on type 'UserConnection'",
"locations" => [{
"line" => 1,
"column" => 11
}],
"path" => ["query", "users", "foobar"],
"extensions" => {
"status" => 400, "code" => "undefinedField", "typeName" => "UserConnection", "fieldName" => "foobar"
}
}]
}
I'd be great if you could share your experiences / resources / best practises when handling HTTP status codes in GraphQL.
GraphQL is transport-agnostic. While GraphQL services are commonly web services that accept requests over HTTP, they can and do accept requests over other transports as well. In fact, a GraphQL service can execute queries with no network requests at all -- all it needs is a query, and, optionally, a variables object and operation name.
Because of this, the GraphQL spec isn't concerned with methods, status codes or anything else specific to HTTP (it only mentions HTTP when discussing serialization). Any practices with regard to these things are at best conventions that have either evolved over time or are simply artifacts from some of the original libraries that were written for GraphQL. As such, any kind of answer to your question is going to be mostly based on opinion.
That said, because your GraphQL service shouldn't care about how its queries are received, arguably there should be a separation between its code and whatever code is handling receiving the requests and sending back the responses (like an Express app in Node.js). In other words, we could say it's never ok for your resolver code to mutate things like the response's status code. This is the current thinking in the community and most libraries only return one of two codes -- 400 if the request itself is somehow invalid and 200 otherwise.
If your entire GraphQL endpoint is guarded by some authentication logic (say your server checks for some header value), then a GraphQL request might come back with a 401 status. But this is something we handle at the web server level, not as part of your schema. It's no different if something went terribly wrong with your web server code and it had to return a 500 status, or the nginx server sitting in front of your returned a 494 (Request header too large), etc.
Traditionally, errors encountered during execution should be thrown and that's it. GraphQL extensions can be used to provide additional context when the errors are collected and serialized -- the name of the error, the stack trace, etc. However, it makes little sense to include HTTP status codes with these errors when, again, the errors have nothing to do with HTTP. Doing so unnecessarily mixes unrelated concepts -- if you want to identify the type of error, you're better off using descriptive codes like GENERIC_SERVER, INVALID_INPUT, etc.
However, conventions around error handling are also changing. Some services want to better distinguish client errors from other execution errors. It's becoming more common to see validation errors or other errors that would be shown to the end user to be returned as part of the data instead of being treated like an execution error.
type Mutation {
login(username: String!, password: String!): LoginPayload!
}
type LoginPayload {
user: User
error: Error
}
You can see payload types like these in action with public APIs like Shopify's. A variant on this approach is to utilize unions to represent a number of possible responses.
type Mutation {
login(username: String!, password: String!): LoginPayload!
}
union LoginPayload = User | InvalidCredentialsError | ExceededLoginAttemptsError
The end result is that the client errors are strongly typed and easily distinguishable from other errors that the end user doesn't care about. There's a lot of benefits to adopting these sort of conventions, but whether they are the right fit for your server is ultimately up to you.

Mixing of schema-level and app-level errors in GraphQL

While building a new application on top of a graphql API we have run into the following problem:
We have a mutation with an input field whose type is a custom scalar with its own validation rules (in this case that the input is a well-formed email address).
On the client, the user of the app fills in a bunch of fields and hits submit. Currently, validation of the email address is handled by the GraphQL layer and aborts the mutation if it fails with a top-level error. Validation of all other fields is handled by the mutation, returning app-level errors in the mutation payload. The other validations in this case cannot be represented directly in the schema since they involve inter-dependent fields.
This behaviour is really unhelpful for the client: it now has to know about errors in two possible locations (top-level graphql errors, and the application errors in the mutation payload) and in two possible formats. It also means that other malformed fields whose malformed-ness is not represented in the GraphQL schema will not be reported until all the schema-level issues have been fixed, forcing the user to go through multiple rounds of "fix the error, hit submit, get another error".
What is the standard solution to this problem? Putting validations (quite complex in this case) on the client? Weakening the schema in order to group all relevant validations at the application layer?
The problem with error categorization
top-level graphql errors, and the application errors in the mutation payload
The distinction that you made between schema-level and application level errors is based on GraphQL type and mutation implementation. A client-side application usually expects a higher abstraction level of errors, i.e., it needs to distinguish user errors and system errors. That way it can mask the system errors as "internal error" and present the user errors as necessary. The developer also can inspect the set of system errors.
See a nice and concise article by Konstantin Tarkus on this: Validation and User Errors in GraphQL Mutations, whose approach I have followed in this answer.
A Not-so-standard-yet-makes-sense solution
To the best of my knowledge, there is no particular standard approach. However, you can try out the following approach.
First, having system-level errors in the top-level field errors of mutation response:
{
"data": {
"viewer": {
"articles": {
"edges": [
{
"node": {
"title": "Sample article title",
"tags": null
}
}
]
}
}
},
"errors": [
{
"message": "Cannot read property 'bar' of undefined",
"locations": [
{
"line": 7,
"column": 11
}
]
}
]
}
Second, putting user-level errors as a separate field errors in mutation payload. Example from the mentioned article:
{
data: {
user: null,
errors: [
'',
'Failed to create a new user account.',
'email',
'User with this email address was already registered.',
]
}
}
// The errors field is just an array of strings that follows this pattern —
// [argumentName1, errorMessage1, argumentName2, errorMessage2, … ]
The above approach enables the client to look for user errors in a defined format in a single place - the errors field of mutation payload. It also allows the client to receive all errors together.
This approach loses automatic validation of the mutation's input type. However, validation is not compromised as the input type's validation logic can be put in a separate function. This function will return validation errors as necessary, which will eventually be put in mutation payload's errors field.
By the way, thanks for the well-written question!
If you are using Apollo, you can easily multiplex the errors array in the graphql response for both graphql errors AND custom errors that are machine readable using this package:
https://github.com/thebigredgeek/apollo-errors

Resources