Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS NodeJS SDK V3 DynamoDB UpdateItem - TypeError: Cannot read property '0' of undefined

I am trying to get a basic database update operation to work in nodejs using the new AWS SDK V3 for NodeJS.

The data object that I am trying to update looks like this:

{
  auth: { BOOL: false },
  username: { S: 'siegbert' },
  secondsLeft: { N: 49985 },
  userid: { S: '123456' }
}

In the same file I have already succesfully done a GetItemCommand using the SDK V3.

Unfortunately I keep getting a really weird error when using the AWS SDK v3, when using the SDK v2, the exact same params seem to work. I tried looking into the docs but the update operation is not really well documented yet.

var params = {
    TableName: "tableXYZ",
    Key: {
        userid: user.userid.S,
    },
    UpdateExpression: "SET secondsLeft = :newsecondsLeft", 
    ExpressionAttributeValues: { 
        ":newsecondsLeft": user.secondsLeft.N,
    },
    ReturnValues: "UPDATED_NEW"
};


try {
        const data = await dbclient.send(new UpdateItemCommand(params));
        console.log("data:" + JSON.stringify(data));
        return true;
    } catch (error) {
        console.error(error);
        return false;
    }

This basically throws

TypeError: Cannot read property '0' of undefined
    at Object.AttributeValue.visit (XX\node_modules\@aws-sdk\client-dynamodb\dist\cjs\models\models_0.js:1101:40)
    at XX\node_modules\@aws-sdk\client-dynamodb\dist\cjs\protocols\Aws_json1_0.js:5074:20
    at Array.reduce (<anonymous>)
    at serializeAws_json1_0ExpressionAttributeValueMap (XX\node_modules\@aws-sdk\client-dynamodb\dist\cjs\protocols\Aws_json1_0.js:5068:34)
    at serializeAws_json1_0UpdateItemInput (XX\node_modules\@aws-sdk\client-dynamodb\dist\cjs\protocols\Aws_json1_0.js:6067:40)
    at Object.serializeAws_json1_0UpdateItemCommand (XX\node_modules\@aws-sdk\client-dynamodb\dist\cjs\protocols\Aws_json1_0.js:474:27)
    at serialize (XX\node_modules\@aws-sdk\client-dynamodb\dist\cjs\commands\UpdateItemCommand.js:42:30)
    at XX\node_modules\@aws-sdk\middleware-serde\dist\cjs\serializerMiddleware.js:5:27
    at XX\node_modules\@aws-sdk\middleware-logger\dist\cjs\loggerMiddleware.js:6:28

When using the exact same params but with the SDK v2, it works:

var docClient = new AWS.DynamoDB.DocumentClient();

docClient.update(params, function (err, data) {
    if (err) {
        console.error("Unable to update item. Error JSON:", JSON.stringify(err, null, 2));
    } else {
        console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));
    }
});

Any help on how to use the SDK V3 for the update would be appreciated!

like image 527
Dusdoron Avatar asked Mar 11 '21 22:03

Dusdoron


3 Answers

Couple of corrections:

  • when passing values we need to pass the object with type. so, instead of user.userid.S pass entire user.userid. Since its not able to determine the type, it is assuming as an array and trying to get the first element of the array and resulting in that error.
  • Numeric values, should simply be passed as String value of type 'N',like secondsLeft: { N: "49985" }

Here is the updated code.

const { DynamoDB, UpdateItemCommand } = require("@aws-sdk/client-dynamodb");
const dbclient = new DynamoDB({ region: "us-east-1" });
const user = {
  auth: { BOOL: false },
  username: { S: "siegbert" },
  secondsLeft: { N: "49985" },
  userid: { S: "123456" },
};
var params = {
  TableName: "tableXYZ",
  Key: {
    id: user.userid,
  },
  UpdateExpression: "SET secondsLeft = :newsecondsLeft",
  ExpressionAttributeValues: {
    ":newsecondsLeft": user.secondsLeft,
  },
  ReturnValues: "UPDATED_NEW",
};

dbclient
  .send(new UpdateItemCommand(params))
  .then((result) => {
    console.log("data:" + result);
  })
  .catch((err) => {
    console.log("err", err);
  });
like image 157
Balu Vyamajala Avatar answered Oct 24 '22 04:10

Balu Vyamajala


You can use the @aws-sdk/lib-dynamodb package, which is a closer replacement to the v2 style of aws-sdk. From the documentation:

The document client simplifies working with items in Amazon DynamoDB by abstracting away the notion of attribute values. This abstraction annotates native JavaScript types supplied as input parameters, as well as converts annotated response data to native JavaScript types.

This is perhaps a cleaner alternative (and easier to upgrade to) to using @aws-sdk/client-dynamodb directly, although you will still need to instantiate that client as an input to using @aws-sdk/lib-dynamodb.

Here is their example code:

import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; // ES6 import
// const { DynamoDBClient } = require("@aws-sdk/client-dynamodb"); // CommonJS import
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb"; // ES6 import
// const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb"); // CommonJS import

const client = new DynamoDBClient({});
const ddbDocClient = DynamoDBDocumentClient.from(client);

await ddbDocClient.put({
  TableName,
  Item: {
    id: "2",
    content: "content from DynamoDBDocument",
  },
});
like image 5
Brent L Avatar answered Oct 24 '22 04:10

Brent L


Using the DynamoDBDocumentClient from the lib-dynamodb module makes it easier:

import {DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";

const test = async () => {

    const client = new DynamoDBClient({region: 'eu-west-1'})

    const params = {
        TableName: "tableXYZ",
        Item: 
            {
            userid: 'userId',
                ...user
        }
    }

    const docClient = DynamoDBDocumentClient.from(client);

    try {
        const response = await docClient.send(new PutCommand(params))
        console.log(response)
    } catch (e) {
        console.error(e)
    }
}

test()

(this can be run as a stand-alone node script)

like image 2
Ciryon Avatar answered Oct 24 '22 05:10

Ciryon