artemis
artemis copied to clipboard
How to handle generic Error interface in Union result
Our schema looks like this:
type Query {
courier(code: String!): CourierResult!
}
union CourierResult = Courier | CourierNotFoundError | InternalServerError
type Courier {
name: String!
code: String!
picture: String!
}
interface Error {
message: String!
}
type InternalServerError implements Error {
message: String!
}
type CourierNotFoundError implements Error {
message: String!
}
This schema was inspired by this blog post: https://blog.logrocket.com/handling-graphql-errors-like-a-champ-with-unions-and-interfaces/
When consuming this schema, we use the following query:
query courier($code: String!) {
courier(code: $code) {
__typename
... on Courier {
name
code
picture
}
... on CourierNotFoundError {
message
}
... on Error {
message
}
}
}
This way, ... on Error is a catch-all for errors. If we add a new error in the future, backend will return it and even an outdated frontend should be able to handle it as a generic Error until we implement a specific logic for it. For instance, InternalServerError should be treated as Error as the query does not know it in advance, but the API returned it.
What Artemis currently does is the following:
class Courier$Query$CourierResult with EquatableMixin {
Courier$Query$CourierResult();
factory Courier$Query$CourierResult.fromJson(Map<String, dynamic> json) {
switch (json['__typename'].toString()) {
case r'Courier':
return Courier$Query$CourierResult$Courier.fromJson(json);
case r'CourierNotFoundError':
return Courier$Query$CourierResult$CourierNotFoundError.fromJson(json);
case r'Error':
return Courier$Query$CourierResult$Error.fromJson(json);
default:
}
return _$Courier$Query$CourierResultFromJson(json);
}
// ...
}
When receiving a __typename: InternalServerError, no case will be matched and it will return a generic Courier$Query$CourierResult which is not convertible to Courier$Query$CourierResult$Error and does not have the message property. This way, we may lose valuable information
What I propose is to add some kind of logic for detecting types which extend an interface, and queries which use interfaces.
I suppose I can manually create a custom parser for this.
Specs Artemis version: 6.15.1-beta.1
build.yaml:
targets:
$default:
builders:
artemis:
options:
schema_mapping:
- output: lib/modules/graphql/api.graphql.dart
schema: lib/modules/graphql/schema.graphql
queries_glob: lib/modules/graphql/queries/**.graphql
Artemis output:
# Please paste the output of
$ flutter pub run build_runner build --verbose
#or
$ pub run build_runner build --verbose
GraphQL schema:
type Query {
courier(code: String!): CourierResult!
}
union CourierResult = Courier | CourierNotFoundError | InternalServerError
type Courier {
name: String!
code: String!
picture: String!
}
interface Error {
message: String!
}
type InternalServerError implements Error {
message: String!
}
type CourierNotFoundError implements Error {
message: String!
}
GraphQL query:
query courier($code: String!) {
courier(code: $code) {
__typename
... on Courier {
name
code
picture
}
... on CourierNotFoundError {
message
}
... on Error {
message
}
}
}
Hi. Interesting case.
May be this could be handled by utilizing default: case...
I'll try to create reproduction and think about the fix