Search code examples
amazon-web-servicesaws-lambdaamazon-dynamodbdynamodb-queries

AWS DynamoDB BatchWriteCommand fails with ValidationException with marshalled data in typescript


I am trying to persist marshalled data in AWS DynamoDB using BatchWriteCommand from AWS Lambda. However it fails with ValidationException :

{
  "errorType": "ValidationException",
  "errorMessage": "The provided key element does not match the schema",
  "trace": [
    "ValidationException: The provided key element does not match the schema",
    "    at throwDefaultError (/var/runtime/node_modules/@aws-sdk/smithy-client/dist-cjs/default-error-handler.js:8:22)",
    "    at deserializeAws_json1_0BatchWriteItemCommandError (/var/runtime/node_modules/@aws-sdk/client-dynamodb/dist-cjs/protocols/Aws_json1_0.js:665:51)",
    "    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)",
    "    at async /var/runtime/node_modules/@aws-sdk/middleware-serde/dist-cjs/deserializerMiddleware.js:7:24",
    "    at async /var/runtime/node_modules/@aws-sdk/lib-dynamodb/dist-cjs/baseCommand/DynamoDBDocumentClientCommand.js:18:34",
    "    at async /opt/nodejs/node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-signing/dist-cjs/middleware.js:13:20",
    "    at async StandardRetryStrategy.retry (/opt/nodejs/node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-retry/dist-cjs/StandardRetryStrategy.js:51:46)",
    "    at async /opt/nodejs/node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-logger/dist-cjs/loggerMiddleware.js:6:22",
    "    at async Runtime.handler (/var/task/main.js:95:22)"
  ]
}

Code:

export interface MyEvent {
    eventId: number
    eventTime: number
}

export interface MyModel {
    category: string
    status: string
    details: MyEvent[]
}

const dbClient = new DynamoDB({ apiVersion: "2012-08-10", "ap-southeast-2" })
export const ddbDocClient = DynamoDBDocument.from(dbClient)

export async function save(myItems: MyModel[]): Promise<void> {
    const marshalledData = myItems.map(event => ({
        PutRequest: {
            Item: marshall({
                myPK: "111111",
                mySortKey: event.category,
                status: event.status,
                eventDetails: event.details
            })
        }
    }))
    const command = new BatchWriteCommand({
        RequestItems: {
            ["events"]: marshalledData
        }
    })
    try {
        await ddbDocClient.send(command)
    } catch (e) {
        throw Error(`Error ${(<Error>e).message} `)
    }
}

Can anyone please help me understand what could be the issue? If I marshall only the eventDetails like below, then the data gets persisted, however the data is not saved as MapAttribute:

eventDetails: marshall(event.details)

Data gets saved as:

[ { "M" : { "M" : { "M" ....

Correct way should be:

{
    L: [
      {
        M: {

Thanks

Update: Please find the stringify of the marshalledData:

{
    category: { S: 'Accounts' },
    status: { S: 'Active' },
    details: {
        L: [
            {
                M: {
                    eventId: { N: '101' },
                    eventTime: { N: '1686369780' }
                }
            },
            {
                M: {
                    eventId: { N: '102' },
                    eventTime: { N: '1686371880' }
                }
            }
        ]
    }
}

Solution

  • Thanks for the update, I see your issue now. You are using the Document Client but you are also using marshall which in turn is causing you to double on the DynamoDB-JSON. Document Client marshalls by default, thats what makes it useful. Try:

        const marshalledData = myItems.map(event => ({
            PutRequest: {
                Item:{
                    myPK: "111111",
                    mySortKey: event.category,
                    status: event.status,
                    eventDetails: event.details
                }
            }
        }))