2

I am trying to execute a simple GraphQL query in my Flutter project. The goal is to get a list of customers with a nested list of devices. However, the results are not being parsed properly and i found some inconsistency between appsync and my project appsync calls. I am pretty new to aws and so i am not familiar how to approach this problem the best way. It would be great if someone can guide me a little here :).

I have created a backend using Amplify Studio and imported it into my project. I created the models in my project with "amplify codegen models".

Here is the Schema:

type Customer @model @auth(rules: [{allow: public}]) {
  id: ID!
  devices: [Device] @hasMany(indexName: "byCustomer", fields: ["id"])
  first_name: String
  last_name: String
  email: AWSEmail!
}

type Device @model @auth(rules: [{allow: public}]) {
  id: ID!
  sessions: [Session] @hasMany(indexName: "byDevice", fields: ["id"])
  customer_id: ID @index(name: "byCustomer")
  thing_name: String!
}

Running the desired query in appsync

query MyQuery {
  listCustomers {
    items {
      id
      email
      first_name
      last_name
      devices {
        items {
          id
          thing_name
          customer_id
        }
      }
    }
  }
}

gives me this response, where devices is an object with the items property which is an array of devices:

{
  "data": {
    "listCustomers": {
      "items": [
        {
          "id": "585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c",
          "email": "[email protected]",
          "first_name": "Max",
          "last_name": "Mustermann",
          "devices": {
            "items": [
              {
                "id": "ca7bf7f9-b0c7-4a83-a7cc-a7b7d0678db7",
                "thing_name": "Device_002",
                "customer_id": "585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c"
              },
              {
                "id": "24967d96-29b9-4ef9-b6d2-684002166a40",
                "thing_name": "Device_003",
                "customer_id": "585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c"
              }
            ]
          }
        },
        {
          "id": "fdcca2c9-ec12-4ce2-a439-15f14bc00d55",
          "email": "[email protected]",
          "first_name": "inautequiconsec",
          "last_name": "mollitametmagnaaliq",
          "devices": {
            "items": [
              {
                "id": "520d9441-d026-45b0-9c3c-26f848e8e728",
                "thing_name": "Device_001",
                "customer_id": "fdcca2c9-ec12-4ce2-a439-15f14bc00d55"
              }
            ]
          }
        }
      ]
    }
  }
}

Running the same query from my flutter project:

  Future<void> _refreshCustomerList() async {
    const listCustomers = 'listCustomers';
    const graphQLDocument = '''query MyQuery {
      $listCustomers {
        items {
          id
          email
          first_name
          last_name
          devices {
            items {
              id
              thing_name
              customer_id
            }
          }
        }
      }
    }''';

    final listCustomersRequest = GraphQLRequest<PaginatedResult<Customer>>(
      document: graphQLDocument,
      modelType: const PaginatedModelType(Customer.classType),
      decodePath: listCustomers,
    );

    try {
      final response =
          await Amplify.API.query(request: listCustomersRequest).response;

      if (response.hasErrors) {
        safePrint('errors: ${response.errors}');
        return;
      }

      final data = response.data;
      final items = response.data?.items;
      safePrint('response: $response');
      safePrint('data: $data');
      safePrint('items: $items');

      setState(() {
        // set _customerList
      });
    } on ApiException catch (e) {
      safePrint('Query failed: $e');
    }
  }

results in this logs, where devices is not an object with items in it but an array of devices itself:

flutter: response: GraphQLResponse<PaginatedResult<Customer>> success: {
  "items": [
    {
      "id": "585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c",
      "devices": [
        {
          "id": "ca7bf7f9-b0c7-4a83-a7cc-a7b7d0678db7",
          "sessions": null,
          "customer_id": "585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c",
          "thing_name": "Device_002",
          "createdAt": null,
          "updatedAt": null
        },
        {
          "id": "24967d96-29b9-4ef9-b6d2-684002166a40",
          "sessions": null,
          "customer_id": "585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c",
          "thing_name": "Device_003",
          "createdAt": null,
          "updatedAt": null
        }
      ],
      "first_name": "Max",
      "last_name": "Mustermann",
      "email": "[email protected]",
      "createdAt": null,
      "updatedAt": null
    },
    {
      "id": "fdcca2c9-ec12-4ce2-a439-15f14bc00d55",
      "devices": [
        {
          "id": "520d9441-d026-45b0-9c3c-26f848e8e728",
          "sessions": null,
          "customer_id": "fdcca2c9-ec12-4ce2-a439-15f14bc00d55",
          "thing_name": "Device_001",
          "createdAt": null,
          "updatedAt": null
        }
      ],
      "first_name": "inautequiconsec",
      "last_name": "mollitametmagnaaliq",
      "email": "[email protected]",
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "nextToken": null
}
flutter: data: Instance of 'PaginatedResult<Customer>'
flutter: items: [Customer {id=585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c, first_name=Max, last_name=Mustermann, [email protected], createdAt=null, updatedAt=null}, Customer {id=fdcca2c9-ec12-4ce2-a439-15f14bc00d55, first_name=inautequiconsec, last_name=mollitametmagnaaliq, [email protected], createdAt=null, updatedAt=null}]

The problem now is, that those devices are not getting parsed properly to be in the Customer Object as seen in the logs. The generated Customer.fromJson looks like this:

  Customer.fromJson(Map<String, dynamic> json)
      : id = json['id'],
        _devices = json['devices'] is List
            ? (json['devices'] as List)
                .where((e) => e?['serializedData'] != null)
                .map((e) => Device.fromJson(
                    new Map<String, dynamic>.from(e['serializedData'])))
                .toList()
            : null,
        _first_name = json['first_name'],
        _last_name = json['last_name'],
        _email = json['email'],
        _createdAt = json['createdAt'] != null
            ? amplify_core.TemporalDateTime.fromString(json['createdAt'])
            : null,
        _updatedAt = json['updatedAt'] != null
            ? amplify_core.TemporalDateTime.fromString(json['updatedAt'])
            : null;

I think the problem is, that for whatever reason the .fromJson function looks for a 'serializedData' but there is no property with that name in the response object. I tried getting rid of the check for serializedData but got multiple other errors.

I am really confused why the 'amplify codegen models' created the class this way and i am not sure if i should just edit the .fromJson method to a state where it works or if this will cause other issues down the line.

I also tried the call with ModelQueries.list. There i even dont get any devices in my response. I dont know why, and i am not sure if that may be part of the problem:

  Future<void> _getCustomerList() async {
    try {
      final request = ModelQueries.list(Customer.classType);
      final response = await Amplify.API.query(request: request).response;

      final items = response.data?.items;
      if (response.hasErrors) {
        safePrint('errors: ${response.errors}');
        return;
      }

      safePrint('response: $response');
      safePrint('items: $items');

      setState(() {
        //  // set _customerList
      });
    } on ApiException catch (e) {
      safePrint('Query failed: $e');
    }
  }

Logs:

Reloaded 1 of 2701 libraries in 1.425ms (compile: 44 ms, reload: 619 ms, reassemble: 707 ms).
flutter: response: GraphQLResponse<PaginatedResult<Customer>> success: {
  "items": [
    {
      "id": "585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c",
      "devices": null,
      "first_name": "Max",
      "last_name": "Mustermann",
      "email": "[email protected]",
      "createdAt": "2023-10-01T19:07:43.072000000Z",
      "updatedAt": "2023-10-03T17:00:34.083000000Z"
    },
    {
      "id": "fdcca2c9-ec12-4ce2-a439-15f14bc00d55",
      "devices": null,
      "first_name": "inautequiconsec",
      "last_name": "mollitametmagnaaliq",
      "email": "[email protected]",
      "createdAt": "2023-10-01T19:07:43.125000000Z",
      "updatedAt": "2023-10-01T19:07:43.125000000Z"
    }
  ],
  "nextToken": null
}
flutter: items: [Customer {id=585a6b7e-e3f5-4cd6-9e7b-f1860f559e5c, first_name=Max, last_name=Mustermann, [email protected], createdAt=2023-10-01T19:07:43.072000000Z, updatedAt=2023-10-03T17:00:34.083000000Z}, Customer {id=fdcca2c9-ec12-4ce2-a439-15f14bc00d55, first_name=inautequiconsec, last_name=mollitametmagnaaliq, [email protected], createdAt=2023-10-01T19:07:43.125000000Z, updatedAt=2023-10-01T19:07:43.125000000Z}]

1 Answer 1

0

I had to deal with this issue recently. I posted the solution I found on github.

https://github.com/aws-amplify/amplify-flutter/issues/816#issuecomment-1878191851

But in summary you can do something like this:

class CustomerParser extends ModelType<Customer> {
  @override
  Customer fromJson(Map<String, dynamic> jsonData) {
    return Customer(
      id: jsonData['id'] as String,
      devices: (jsonData['devices'] as List<dynamic>).map((e) => Device.fromJson(e as Map<String, dynamic>).toList(),
      first_name: jsonData['first_name'] as String,
      last_name: jsonData['last_name'] as String,
      email: jsonData['email'] as String,
    );
  }
}

And then change your request by:

final listCustomersRequest = GraphQLRequest<PaginatedResult<Customer>>(
      document: graphQLDocument,
      modelType: const PaginatedModelType(CustomerParser()),
      decodePath: listCustomers,
    );
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.