Amplify-js: DataStore @connection directive on optional field

Created on 14 May 2020  路  7Comments  路  Source: aws-amplify/amplify-js

Describe the bug
Optional fields with @connection directive appear to be incompatible with DataStore. Valid instances can't be saved/synced. My apologies if I'm making a basic error as I'm new to DataStore, AppSync and GraphQL.

Amplify CLI Version
4.19.0

To Reproduce
Consider this simple schema:

type Lecture @model {
  id: ID!
  title: String!
  blurb: String
  presenter: Presenter @connection(name: "LecturePresenters")
}
type Presenter @model {
  id: ID!
  name: String!
  lectures: [Lecture] @connection(name: "LecturePresenters")
}

It seems quite straightforward and similar to other schemas given in the docs. However, this schema does not seem to be able to be used in DataStore. The presenter field on Lecture is deliberately optional. In the real world, a lecture may be created and a presenter found afterwards for the lecture.

Creating a Lecture without presenter DataStore will throw an error when syncing, eg.:

await DataStore.save(new Lecture({title: "My First Lecture", blurb: null}));

Will throw:

One or more parameter values were invalid: Type mismatch for Index Key lecturePresenterId Expected: S Actual: NULL IndexName: gsi-LecturePresenters

The POST request is:

{"query":"mutation operation($input: CreateLectureInput!) {\n createLecture(input: $input) {\n id\n title\n blurb\n _version\n _lastChangedAt\n _deleted\n presenter {\n id\n _deleted\n }\n }\n}\n","variables":{"input":{"id":<some-id>,"title":"My First Lecture","blurb":null,"lecturePresenterId":null}}}: ""

The same result if I pass in 'presenter: null' to new Lecture(). I was not expecting to have to provide a (null) value for 'blurb' in new Lecture() either but it seems that DataStore requires all fields to be provided even if they are optional (requires null value at least).

Expected behavior
Schema would be compatible with DataStore.

DataStore to-be-reproduced

Most helpful comment

@undefobj Thanks so much Richard. I've followed the updated docs and this schema works with DataStore and Amplify CLI 4.20.0:

type Lecture @model
  @key(name: "byPresenter", fields: ["presenterID", "title"]) {
  id: ID!
  title: String!
  blurb: String
  presenterID: ID
}
type Presenter @model {
  id: ID!
  name: String!
  lectures: [Lecture] @connection(keyName: "byPresenter", fields: ["id"])
}

I believe this has solved my issue.

All 7 comments

@dazweeja Might be related to https://github.com/aws-amplify/amplify-js/issues/5040. I've been unable to use optional lists in the DataStore.

@mdoesburg I'm not sure if your example is further complicated by the fact that string and binary type attributes must have lengths greater than zero in DynamoDB?

In my case, I think the root cause is even more obvious. DataStore creates and/or requires all attributes of an item/model while the mutation does not. For example, using the automatically-generated mutation:

await API.graphql(graphqlOperation(mutations.createLecture, {input: {title: 'My First Lecture'}}));

The item is:

{
  title: 'My First Lecture"
  ...
}

And saves to the database fine while DataStore creates a model with all attributes, even the optional ones (I had to pass in the 'blurb: null' value too):

{
  title: 'My First Lecture',
  blurb: null,
  presenter: null,
  ...
}

And it's the null value for presenter that's causing the issue.

I was torn between creating a general issue (DataStore requiring or creating all attributes, even optional ones) or this specific one and I decided to focus on a specific schema which I think should be able to be used in DataStore. But I could be wrong about that because I don't have a good understanding of NoSQL/DynamoDB concepts in general, coming from a SQL background.

This looks like an issue with the JS Library - transferring this issue to the JS team.

I don't believe this is an issue with the JS library. You can't use this schema with Android either. As far as I can tell it's impossible to create a Lecture without a presenter attribute because DataStore requires a value for every attribute, regardless of whether they are optional or not, and for an attribute with a @connection directive, null is an invalid value.

In Android, it gives the error:

com.amplifyframework.AmplifyException: An invalid field was provided - presenter is not present in Lecture

Whether you use:

Lecture lecture = Lecture.builder()
          .title("My First Lecture")
          .build();

Or:

Lecture lecture = Lecture.builder()
          .title("My First Lecture")
          .blurb(null)
          .presenter(null)
          .build();

Can you take a look at the latest docs for Relational modeling with DataStore and use the latest library version? We made some improvements to the CLI, the library on how it handles this internally, and tried to be explicit about schemas in the documentation including how to use @key.

@undefobj Thanks so much Richard. I've followed the updated docs and this schema works with DataStore and Amplify CLI 4.20.0:

type Lecture @model
  @key(name: "byPresenter", fields: ["presenterID", "title"]) {
  id: ID!
  title: String!
  blurb: String
  presenterID: ID
}
type Presenter @model {
  id: ID!
  name: String!
  lectures: [Lecture] @connection(keyName: "byPresenter", fields: ["id"])
}

I believe this has solved my issue.

Great to hear! Have a nice weekend.

Was this page helpful?
0 / 5 - 0 ratings