Ad

JSON Fails To Parse GraphQL Response After Flutter Libs Upgrade

- 1 answer

I upgraded packages in pubspec.yaml and it looks like JSON-parsing for web responses is broken now. I migrated from graphql: ^3.1.0 to graphql: ^4.0.1 and I also use json_annotation: ^3.1.0.

Here is a good example of the response I get from http client and what I see when I try to get it as a string: enter image description here

As you can see roles array is not empty and it's 0 value is not null, but since I upgraded my libraries that's what I get without changing any other code.

Here is my GraphQL query file:

query($pushCredential: TwilioPushCredential!) {
  user {
    id
    info {
      ...UserInfoFragment
    }
    roles {
      ... on Client {
        ...RoleUserFragment
        myFitnessPalId
      }
      ... on Coach {
        ...RoleUserFragment
        maxActiveClients
        inviteCode
      }
    }
    chatJwt(pushCredential: $pushCredential)
  }
}

fragment UserInfoFragment on UserInfo {
  email
  firstName
  lastName
  avatar
  phone
}

fragment RoleUserFragment on RoleUser {
  id
  role
}

Here is how I handle the response:

    final options = QueryOptions(
      document: get_user_info.document,
      variables: {'pushCredential': Platform.isIOS ? 'apn' : 'fcm'},
    );

    final result = await client.query(options);

    Logger.debug("getUser response: ${result.data['user']}");

It looks like this ... on Client and ... on Coach logic doesn't work in the new GraphQL version. How to fix it and what am I doing wrong?

Ad

Answer

This error is caused by the normalize package update in the graphql_flutter, in particular, normalize requires you to specify the possible types map for fragments to work correctly. This is a mapping from the abstract union and interface types to their concrete object types.

interface PersonI {
  name: String
  age: Int
}
type Employee implements PersonI {
  name: String
  age: Int
  daysOfEmployement: Int
}
type InStoreCustomer implements PersonI {
  name: String
  age: Int
  numberOfPurchases: Int
}
type OnlineCustomer implements PersonI {
  name: String
  age: Int
  numberOfPurchases: Int
}
union CustomerU = OnlineCustomer | InStoreCustomer

the possible types map would be:

const POSSIBLE_TYPES = const {
  'CustomerU': {'InStoreCustomer', 'OnlineCustomer'},
  'PersonI': {'Employee', 'InStoreCustomer', 'OnlineCustomer'},
}
// Here's how it's parsed to the cache
final client = GraphQLClient(
  cache: GraphQLCache(
    possibleTypes: POSSIBLE_TYPES,
  ),
)

You can generate the POSSIBLE_TYPES map, e.g., using graphql_codegen.

Furthermore, for normalize to correctly resolve the type you should always make sure you're querying the __typename. Given the example above a query could look something like

query {
  people {
    __typename # Needed to decide where which entry to update in the cache
    ... on Employee {
      name
      age
    }
    ... on Customer {
      name
      age
    }
  }
}

if you're not providing the possible type map and introspecting the typename, the cache can't be updated.

Ad
source: stackoverflow.com
Ad