amplify-cli: reusing an existing ID field in a connection leads to error: All fields provided to an @connection must be non-null scalar or enum fields.

Which Category is your question related to? AppSync / GraphQL

Amplify CLI Version 4.12.0

What AWS Services are you utilizing? AppSync, DynamoDB, Cognito, S3

Provide additional details e.g. code snippets Given an existing model:

type Order
  @model
  @key(
    name: "ByUser"
    fields: ["userId", "createdAt"]
    queryField: "ordersByUser"
  )
  id: ID!
  …
  userId: ID
}

Why would the addition of a new connection like this

  user: User @connection(fields: ["userId"])

Lead to the error: All fields provided to an @connection must be non-null scalar or enum fields.

When adding the connection without the fields parameter, the generated orderUserId would have the same properties as the already existing userId, but it would work with the connection.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 38
  • Comments: 33 (6 by maintainers)

Most upvoted comments

+1 this is a major oversight and prevents a model from having optional connections

I guess a workaround could be having a dummy instance of the type you need to connect to and use it as a default connection in the case in which you would actually need it to be null.

Nonetheless this is a major feature that needs to be implemented ASAP.

Bump

@tomhirschfeld

To get around this issue, explicitly declare the amplify internally generated FK property as optional (make sure to use their naming convention so it maps the data correctly):

orderUserId: ID
user: User @connection

The naming convention is: {camel-cased model name - order}{property name with capital - User}Id

When this property has been explicitly declared, it now becomes available in queries, mutations and filters and can be used in @key directives.

Oh boy, this is a scary situation as any changes down the road will invalidate my current data. Sometimes I really regret using Amplify, other times I think “oh this is seriously cool” But I certainly shouldn’t have used it in a production environment !

Hi @raffibag no problem 😃 We’re all trying to figure this out with limited documentation and examples since it’s so new!

Glad to hear you sorted out a solution. Perhaps it may help to point out a few things in your example code above:

  1. Your orderSparkId and orderWidgetId fields would only work if the model they reside in is called Order. But based on what you described, they should be planSparkId and planWidgetId as they reside in a Plan model. The rest of your implementation looks good.
  2. Creating/updating by passing the planSparkId or planWidgetId is the only way I know of that will allow the underlying model to persist the change. The internal resolvers (created due to the @connection directive), will take care of building the appropriate widget and spark data for the graphql response payload.
  3. Setting things up in this way (using planWidgetId and planSparkId) is only necessary when needing OPTIONAL relations. Had spark and widget been mandatory at the time of creating the model, you could have stuck to your original sparkId: ID! implementation

Does this help and make sense?

Hey, do we have any update on this issue or any valid workaround? It’s an important requirement for my app to have optional connections, and simply using @connection without fields create some other problems for me.

@CodySwannGT @hirochachacha Yes really it’s really painfull to deal with these kind of oversight. Having optional connections is often necessary. I meet the same problem. If I specify the field use in connection it should be non nullable. It works if i dont specify it and the generated field is nullable

I have same issue. I have table which have self connected attribute toId, which can be leaved unset, because there is two entities in table: User and Subscription. If I leave it nullable I have error: All fields provided to an @connection must be non-null scalar or enum fields Otherwise I should set toId attribute for User entity, but it will be better to leave it empty and only remember it when I use Subscription entity.

type Entity
    @model(mutations: null, subscriptions: {level: off})
    @key(fields: ["pk", "sk"]) 
{
    pk: ID!
    sk: String!
    toId: ID
    to: Entity @connection(fields: ["toId"])
}

Before it worked fine, but now I can not have @connection with nullable field.

I read the replies so far as a temporary inconsistency, which hopefully will be resolved at some point.

Oh, sorry for misreading. If I understand correctly, you pointed out inconsistency between @key and @connection - @key accepts nullable fields, on the other hand @connection rejects nullable fields. That’s not dup of #1657.

Nullable connection fields are now supported in the latest version of the CLI. Please upgrade to try it out!

Absolutely, makes sense @blydewright. Thanks for the explanation! I went back and tried it out and it worked perfectly - was able to get the optional associations to show up simply by writing the respective ID to the parent model. I didn’t read the previous posts closely enough and assumed “order” was an Amplify directive (or something like that). Note also that camelcase needs to be carried through all the way (i.e., camelCaseId will work but camelCaseID will not).

Hopefully this gets documented, as my concern now is that any breaking changes will likely not get relayed to those who used this as a workaround / solution.

I was creating a nested tree and need to have optional connections.

Yep just hit this, too. +1.

@tomhirschfeld

To get around this issue, explicitly declare the amplify internally generated FK property as optional (make sure to use their naming convention so it maps the data correctly):

orderUserId: ID
user: User @connection

The naming convention is: {camel-cased model name - order}{property name with capital - User}Id

When this property has been explicitly declared, it now becomes available in queries, mutations and filters and can be used in @key directives.

Can you please link to the documentation where this is explained (provided it exists)? I tried what you proposed and it didn’t work.

@hirochachacha The #1657 issue is related in the sense that it would resolve our problem but this is a separate issue and not a duplicate. This is simply two undocumented issues that result in a catch-22 and prevent basic usage of this framework.

  1. You can’t make a model’s primary ID or owner ID non-nullable because then you would need to set it on the client side during object creation, as opposed to leveraging AppSync’s autogeneration. (#1657)

  2. You can’t use a connection for a primary ID or the owner id if the ID is nullable (this issue).

  3. Thus, you can’t use a model’s primary ID if you want to leverage AppSync’s autogeneration or owner id.

  4. Thus, you can’t use appsync autogeneration for ID if your API uses the connection directive.

Thanks for linking the other issue. I fail to see the connection between these two, though. Without the new user connetion our “Order” was behaving as expected. ‘createdAt’ was automatically set on creation, as one would suspect. Isn’t that what #1657 is about?

In this issue, I was asking, why adding a connection claims to require non-null scalar fields, while the provided field (userId) has those properties.