Search code examples
javascriptaws-lambdaamazon-dynamodbunmarshallingdynamodb-queries

Lambda DynamoDocument isn't unmarshalling results


I have a lambda function to fetch some records from DynamoDB. I setup a ddbDocClient like in the AWS docs. I have a ddbDocClient.js file like this:

const { DynamoDBDocumentClient } = require("@aws-sdk/lib-dynamodb");
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");

const REGION = "us-west-2"

const ddbClient = new DynamoDBClient({ region: REGION });

const marshallOptions = {
    convertEmptyValues: false, // false, by default.
    removeUndefinedValues: false, // false, by default.
    convertClassInstanceToMap: false, // false, by default.
};

const unmarshallOptions = {
    wrapNumbers: false, // false, by default.
};

const translateConfig = { marshallOptions, unmarshallOptions };

const ddbDocClient = DynamoDBDocumentClient.from(ddbClient, translateConfig);

module.exports.ddbDocClient = ddbDocClient;

Then I use it to fetch some records:

const { ddbDocClient } = require("./ddbDocClient.js");
const { QueryCommand } = require("@aws-sdk/client-dynamodb");

const listsTableName = "my_list_name";

/// Fetches lists owned by the current user
async function getListIDs(userid) {

    const params = {
        TableName: listsTableName,
        IndexName: 'owner-index',
        KeyConditionExpression: '#owner = :owner',
        ExpressionAttributeNames: { "#owner": "owner" },
        ExpressionAttributeValues: {":owner": { "S": userid }}
    };
    const command = new QueryCommand(params);

    const dynamoData = await ddbDocClient.send(command);
    const lists = dynamoData.Items;
    console.log(lists);
}

The results I get back are like this:

[
  {
    things: { SS: [Array] },
    owner: { S: '0d5e789b-009b-4ba0-80a7-12065d0908b5' },
    collaborator: { S: 'A845E8EF-956B-467D-B57B-6D959BB78C98' },
    id: { S: '0C629140-6651-43E9-8D9F-3E52EBAD94F3' }
  },
  {
    owner: { S: '0d5e789b-009b-4ba0-80a7-12065d0908b5' },
    things: { SS: [Array] },
    collaborator: { S: 'ttttt' },
    id: { S: 'xxxxx89b-009b-4ba0-80a7-12065d0908b5' }
  }
]

I'm pretty sure the Document classes are supposed to unmarshall this, or am I wrong? It even mentions middleware you can use before and after marshalling.

I am having to manually unmarshall which needs extra steps to convert the SS Set's to Array's like this:

const lists = dynamoData.Items;

// From DynamoDB JSON
var unmarshalledLists = lists.map((i) => unmarshall(i));

if (unmarshalledLists.length > 0) {
    // If we have at least 1 list we need to convert the things Set to Array
    unmarshalledLists.forEach(function(list) {
        const things = Array.from(list.things);
        list.things = things;
    });
    return unmarshalledLists;
} else {
    // No results to return
    throw("No lists found");
}

What am I doing wrong to not have it unmarshall automatically? And it there a cleaner solution for converting the SS results to Array's?


Solution

  • You import QueryCommand from the low level client, and you also use low level syntax in your ExpressionAttributeValues. Try this:

    const { ddbDocClient } = require("./ddbDocClient.js");
    import { QueryCommand } from "@aws-sdk/lib-dynamodb";
    
    const listsTableName = "my_list_name";
    
    /// Fetches lists owned by the current user
    async function getListIDs(userid) {
    
        const params = {
            TableName: listsTableName,
            IndexName: 'owner-index',
            KeyConditionExpression: '#owner = :owner',
            ExpressionAttributeNames: { "#owner": "owner" },
            ExpressionAttributeValues: {":owner":  userid }
        };
        const command = new QueryCommand(params);
    
        const dynamoData = await ddbDocClient.send(command);
        const lists = dynamoData.Items;
        console.log(lists);
    }