Roy
Roy

Reputation: 1518

How to avoid creating duplicate items in an array when posting an AWS Amplify GraphQL mutation

Preface: I'm new to GraphQL and Amplify.

I have a user object that contains two arrays, one called preferred_genres, and another called preferred_characters. I have a profile page where the user can edit their profile and add items to these arrays.

The update mutation provided by AWS Amplify seems only to be adding elements to the array, never removing them. If on my UI I remove an item from the list and then submit the update mutation, the item is not removed from my backend. If I add an item to the array, the item is added on the backend, but I also end up with duplicate values for those already in the backend.

What I'm trying to do is overwrite the array on the backend with the data I'm submitting from the front end. Am I missing something obvious? Is there a more appropriate way of achieving this in GraphQL?

Schema

type User @model {
  id: ID!
  ...
  preferred_characters: [Character]
  preferred_genres: [Genre]
  ...
}

Screenshot of Profile showing duplicate items in array Screenshot of database on backend showing duplicate items in array

Upvotes: 3

Views: 3159

Answers (3)

MauricioLeal
MauricioLeal

Reputation: 6751

DataStore's Conflict resolution documentation:

enter image description here

Updating Conflict Resolution:

  1. One way is to update it as the documentation suggests (The way I did it).

enter image description here

  1. I checked which files changed and the only important key to update can be found in amplify/backend/api/<yourApi>/transform.conf.json

From:

enter image description here

To:

enter image description here

Upvotes: 3

Roy
Roy

Reputation: 1518

I stumbled upon this question in the Amplify GitHub issues about the AWSJSON scalar having a similar issue. The issue seems to be related to conflict resolution in the API.

I don't remember setting that up, but I tried amplify update API, chose my GraphQL API and the advanced options, declined any conflict resolution and ran amplify push.

Now values are saving as expected. When I remove an item from an array it's removed from the backend and visa-versa.

Upvotes: 0

Gerard Sans
Gerard Sans

Reputation: 2279

A way to implement this would be to change your updateUser resolver to update or create these entries.

{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },

    ## Set up some space to keep track of things you're updating **
    #set( $expNames  = {} )
    #set( $expValues = {} )
    #set( $expSet = {} )
    #set( $expAdd = {} )
    #set( $expRemove = [] )

    ## Increment "version" by 1 **
    $!{expAdd.put("version", ":one")}
    $!{expValues.put(":one", { "N" : 1 })}

    ## Iterate through each argument, skipping "id" and "expectedVersion" **
    #foreach( $entry in $context.arguments.entrySet() )
        #if( $entry.key != "id" && $entry.key != "expectedVersion" )
            #if( (!$entry.value) && ("$!{entry.value}" == "") )
                ## If the argument is set to "null", then remove that attribute from the item in DynamoDB **

                #set( $discard = ${expRemove.add("#${entry.key}")} )
                $!{expNames.put("#${entry.key}", "$entry.key")}
            #else
                ## Otherwise set (or update) the attribute on the item in DynamoDB **

                $!{expSet.put("#${entry.key}", ":${entry.key}")}
                $!{expNames.put("#${entry.key}", "$entry.key")}
                $!{expValues.put(":${entry.key}", { "S" : "${entry.value}" })}
            #end
        #end
    #end

    ## Start building the update expression, starting with attributes you're going to SET **
    #set( $expression = "" )
    #if( !${expSet.isEmpty()} )
        #set( $expression = "SET" )
        #foreach( $entry in $expSet.entrySet() )
            #set( $expression = "${expression} ${entry.key} = ${entry.value}" )
            #if ( $foreach.hasNext )
                #set( $expression = "${expression}," )
            #end
        #end
    #end

    ## Continue building the update expression, adding attributes you're going to ADD **
    #if( !${expAdd.isEmpty()} )
        #set( $expression = "${expression} ADD" )
        #foreach( $entry in $expAdd.entrySet() )
            #set( $expression = "${expression} ${entry.key} ${entry.value}" )
            #if ( $foreach.hasNext )
                #set( $expression = "${expression}," )
            #end
        #end
    #end

    ## Continue building the update expression, adding attributes you're going to REMOVE **
    #if( !${expRemove.isEmpty()} )
        #set( $expression = "${expression} REMOVE" )

        #foreach( $entry in $expRemove )
            #set( $expression = "${expression} ${entry}" )
            #if ( $foreach.hasNext )
                #set( $expression = "${expression}," )
            #end
        #end
    #end

    ## Finally, write the update expression into the document, along with any expressionNames and expressionValues **
    "update" : {
        "expression" : "${expression}"
        #if( !${expNames.isEmpty()} )
            ,"expressionNames" : $utils.toJson($expNames)
        #end
        #if( !${expValues.isEmpty()} )
            ,"expressionValues" : $utils.toJson($expValues)
        #end
    },

    "condition" : {
        "expression"       : "version = :expectedVersion",
        "expressionValues" : {
            ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
        }
    }
}

More details

Upvotes: 2

Related Questions