How to write data to local DynamoDB? - node.js

According to DynamoDB docs, you always need to specify --endpoint-url http://localhost:8000 option if you want to interact with your local (downloaded) version of DynamoDB. It works fine for me via aws cli, but, following their tutorial for Node.js developers, I've faced a strange issue: I have been interacting with data in DynamoDB web version instead of my local one, even if configured connection options to database:
const serviceConfigOptions: ServiceConfigurationOptions = {
region: "eu-north-1",
endpoint: "http://localhost:8000",
};
I described endpoint, but I have no results interacting with data in my local version and I have these in the web.
All code is here:
import fs from "fs";
import AWS, { AWSError } from "aws-sdk";
import { config as dotenvConfig } from "dotenv";
import {
CreateTableInput,
DocumentClient,
PutItemInput,
PutItemOutput,
} from "aws-sdk/clients/dynamodb";
import { ServiceConfigurationOptions } from "aws-sdk/lib/service";
dotenvConfig();
const serviceConfigOptions: ServiceConfigurationOptions = {
region: "eu-north-1",
endpoint: "http://localhost:8000",
};
const dynamoDB: AWS.DynamoDB = new AWS.DynamoDB();
const params: CreateTableInput = {
TableName: "Movies",
KeySchema: [
{
AttributeName: "year",
KeyType: "HASH",
},
{
AttributeName: "title",
KeyType: "RANGE",
},
],
AttributeDefinitions: [
{ AttributeName: "year", AttributeType: "N" },
{ AttributeName: "title", AttributeType: "S" },
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10,
},
};
dynamoDB.createTable(params, (err, data): void => {
if (err) {
console.log("Unable to create table.");
console.error(err);
return;
}
console.log("Table created");
console.log(data);
});
const docClient: DocumentClient = new AWS.DynamoDB.DocumentClient();
console.log("Imporing data to DynamoDB");
fs.readFile(
"moviedata.json",
"utf-8",
(err: NodeJS.ErrnoException | null, data: string): void => {
if (err) {
console.log("Unable to read a file");
console.error(err);
return;
}
const allMovies: Array<any> = JSON.parse(data);
allMovies.forEach(({ year, title, info }): void => {
const params: PutItemInput = {
TableName: "Movies",
Item: {
year,
title,
info,
},
};
docClient.put(params, (err: AWSError, data: PutItemOutput) => {
if (err) {
console.error(
"Unable to add movie",
title,
". Error JSON:",
JSON.stringify(err, null, 2),
);
return;
}
console.log("PutItem succeeded:", title);
});
});
},
);
UPDATE:
Refactored code into promises.
import fs from "fs";
import { promisify } from "util";
import AWS, { AWSError } from "aws-sdk";
import { config as dotenvConfig } from "dotenv";
import {
CreateTableInput,
DocumentClient,
PutItemInput,
PutItemOutput,
} from "aws-sdk/clients/dynamodb";
import { ServiceConfigurationOptions } from "aws-sdk/lib/service";
dotenvConfig();
const serviceConfigOptions: ServiceConfigurationOptions = {
region: "eu-north-1",
endpoint: "http://localhost:8000",
};
const dynamoDB: AWS.DynamoDB = new AWS.DynamoDB();
const params: CreateTableInput = {
TableName: "Movies",
KeySchema: [
{
AttributeName: "year",
KeyType: "HASH",
},
{
AttributeName: "title",
KeyType: "RANGE",
},
],
AttributeDefinitions: [
{ AttributeName: "year", AttributeType: "N" },
{ AttributeName: "title", AttributeType: "S" },
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10,
},
};
const createDBTable: (
params: AWS.DynamoDB.CreateTableInput,
) => Promise<AWS.DynamoDB.CreateTableOutput> = promisify(
dynamoDB.createTable,
).bind(dynamoDB);
const readFile = promisify(fs.readFile);
createDBTable(params)
.then(data => {
console.log("Table created");
console.log(data);
})
.catch(err => {
console.log("Unable to create table.");
throw new Error(err);
})
.then(() => {
console.log("Imporing data to DynamoDB");
return readFile("moviedata.json", "utf-8");
})
.then((data: string) => {
const allMovies: Array<any> = JSON.parse(data);
const docClient: DocumentClient = new AWS.DynamoDB.DocumentClient();
allMovies.forEach(({ year, title, info }): void => {
const params: PutItemInput = {
TableName: "Movies",
Item: {
year,
title,
info,
},
};
docClient.put(params, (err: AWSError, data: PutItemOutput) => {
if (err) {
console.error(
"Unable to add movie",
title,
". Error JSON:",
JSON.stringify(err, null, 2),
);
return;
}
console.log("PutItem succeeded:", title);
});
});
})
.catch(err => {
console.error(err);
});
Now I'm getting Unable to create table. Error: ResourceInUseException: Table already exists: Movies. However, in my local db only one Music table.
$ aws dynamodb list-tables --endpoint-url http://localhost:8000
Out:
{
"TableNames": [
"Music"
]
}
P.S. AWS_SDK_LOAD_CONFIG=1

I found the mistake I made.
I've configured params
const serviceConfigOptions: ServiceConfigurationOptions = {
region: "eu-north-1",
endpoint: "http://localhost:8000",
};
but I forgot to pass it to const dynamoDB: AWS.DynamoDB = new AWS.DynamoDB(); as an argument.
Must be
const serviceConfigOptions: ServiceConfigurationOptions = {
region: "eu-north-1",
endpoint: "http://localhost:8000",
};
const dynamoDB: AWS.DynamoDB = new AWS.DynamoDB(serviceConfigOptions);

Related

NodeJS ValidationException: The provided key element does not match the schema

I know this has been asked before but I've tried everything from previous posts and still couldn't find a solution for my problem. I can query the table from DynamoDB no problem but can't from lambda. I deleted and created the table with the same name to get rid of a sort key hoping to fix my issue but it did not fix it too. So here is my code
Params
const queryParams = {
TableName: tableName,
KeyConditionExpression: 'postId = :postId',
ExpressionAttributeValues: { ":postId": postId }
};
Schema
type LikeRelationship
#model(
mutations: {
create: "createLikeRelationship"
delete: "deleteLikeRelationship"
update: null
}
timestamps: null
)
#auth(
rules: [
{
allow: owner
ownerField: "likerId"
operations: [read, create, delete]
}
{ allow: public, provider: iam, operations: [read] }
]
)
#key(fields: ["postId"]) {
postId: ID!
postOwnerId: String!
likerType: String
likerName: String
likerId: ID!
timestamp: Int!
}
Full Lambda Code
/* Amplify Params - DO NOT EDIT
API_GROOVESECONDED_GRAPHQLAPIIDOUTPUT
API_GROOVESECONDED_LIKERELATIONSHIPTABLE_ARN
API_GROOVESECONDED_LIKERELATIONSHIPTABLE_NAME
ENV
REGION
Amplify Params - DO NOT EDIT */
exports.handler = async (event, context, callback) => {
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10' });
const tableName = 'LikeRelationship-76mphdcsf5cijbgq5kkcny3xpe-staging';
async function deleteItems(tableName, postId ) {
const queryParams = {
TableName: tableName,
KeyConditionExpression: 'postId = :postId',
ExpressionAttributeValues: { ":postId": postId }
};
const queryResults = await docClient.query(queryParams).promise();
if (queryResults.Items && queryResults.Items.length > 0) {
const batchCalls = chunks(queryResults.Items, 25).map( async (chunk) => {
const deleteRequests = chunk.map( item => {
return {
DeleteRequest : {
Key : {
'partitionId' : item.partitionId,
'sortId' : item.sortId,
}
}
};
}
);
const batchWriteParams = {
RequestItems : {
[tableName] : deleteRequests
}
};
await docClient.batchWrite(batchWriteParams).promise();
});
await Promise.all(batchCalls);
}
}
if (event.body !== null && event.body !== undefined) {
let data = JSON.parse(event.body);
if (typeof data.postId === 'undefined') {
return sendRes(404, '{ error: true, message: "postId undefined." }');
}
try {
await deleteItems(tableName, data.postId );
} catch (e) {
return sendRes(404, '{ error: true, message: " ' + e + data.postId + typeof data.postId + ' " }');
}
return sendRes(200, '{ "error": false, "message": "Success" }');
}
return sendRes(404, '{ error: true, message: "Event body null or undefined." }');
};
const sendRes = (status, body) => {
var response = {
statusCode: status,
headers: {
"Content-Type" : "application/json",
"Access-Control-Allow-Headers" : "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
"Access-Control-Allow-Methods" : "OPTIONS,POST",
"Access-Control-Allow-Credentials" : true,
"Access-Control-Allow-Origin" : "*",
"X-Requested-With" : "*"
},
body: body
};
return response;
};
// https://stackoverflow.com/a/37826698/3221253
function chunks(inputArray, perChunk) {
return inputArray.reduce((all,one,i) => {
const ch = Math.floor(i/perChunk);
all[ch] = [].concat((all[ch]||[]),one);
return all;
}, []);
}
IAM Role
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:Delete*",
"dynamodb:Get*",
"dynamodb:Scan",
"dynamodb:Query"
],
"Resource": [
"arn:aws:dynamodb:eu-central-1:382762466424:table/LikeRelationship-76mphdcsf5cijbgq5kkcny3xpe-staging",
"arn:aws:dynamodb:eu-central-1:382762466424:table/LikeRelationship-76mphdcsf5cijbgq5kkcny3xpe-staging/index/*"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"dynamodb:List*",
"dynamodb:Describe*"
],
"Resource": [
"arn:aws:dynamodb:eu-central-1:382762466424:table/LikeRelationship-76mphdcsf5cijbgq5kkcny3xpe-staging",
"arn:aws:dynamodb:eu-central-1:382762466424:table/LikeRelationship-76mphdcsf5cijbgq5kkcny3xpe-staging/index/*"
]
}
]
}

Apollo Server Resolver not returning all data (returned data is not complete)

My setup: Apollo server with express.js
MongoDB with Mongoose
My problem: When I run a query, my resolver is not fetching all of the data, just part of it.
Here is my resolver code:
getMarsContentForScreen: async (_, { screen, token }, context) => {
if (!context.screen) return {};
console.log(screen, token);
const contentOut = {};
const screenExist = await MarsScreen.findOne({
name: screen,
token: token,
});
if (screenExist) {
const content = await MarsContent.findOne({
screens: { $in: screenExist.id },
});
if (content) {
// ID
contentOut.id = content.id;
// NAME
contentOut.name = content.name;
// ENTRY
contentOut.entry = [{ entryVideos: [] }];
content.entry.map(async (val) => {
let file = await Asset.findById(val, 'uri');
if (file && file.uri) {
contentOut.entry[0].entryVideos.push(
file.uri.split('/').slice(-1)[0]
);
}
});
// EQUIPMENT
contentOut.equipment = [];
content.equipment.map(async (val) => {
let equipment = await MarsEquipment.findById(
val.id,
'name thumbnail background'
);
if (equipment) {
contentOut.equipment.push({
id: val.id,
name: equipment.name,
panelImage: equipment.thumbnail.split('/').slice(-1)[0],
productImage: equipment.background.split('/').slice(-1)[0],
});
}
});
// EXERCISES
contentOut.exercises = [];
content.exercises.map(async (val, index) => {
contentOut.exercises.push({
equipment: val.equipment,
content: [],
});
val.content.map(async (valC) => {
let exercise = await MarsExercise.findById(
valC.id,
'name level text thumbnail video'
);
if (exercise) {
let instructions = [];
for (const [key, value] of Object.entries(
JSON.parse(exercise.text)
)) {
instructions.push(value);
}
contentOut.exercises[index].content.push({
id: valC.id,
position: valC.position,
name: exercise.name,
level: exercise.level,
instructions: instructions,
panelImage: exercise.thumbnail.split('/').slice(-1)[0],
programVideo: exercise.video.split('/').slice(-1)[0],
});
}
});
});
// OPTIONS
contentOut.options = [];
let bgImage = await Asset.findById(content.options[0].bgImage, 'uri');
bgImage = bgImage.uri.split('/').slice(-1)[0];
contentOut.options = [
{
bgImage: bgImage,
cards: [],
},
];
content.options[0].cards.map(async (val, index) => {
let cardImg = await Asset.findById(val.panelImage, 'uri');
if (cardImg) {
contentOut.options[0].cards.push({
name: val.name,
panelImage: cardImg.uri.split('/').slice(-1)[0],
subheading: val.subheading,
action: val.action,
});
if (val.overlay) {
contentOut.options[0].cards[index].overlay = val.overlay;
}
if (
val.externalApp &&
val.externalApp.appName &&
val.externalApp.playStoreId
) {
contentOut.options[0].cards[index].externalApp = {
appName: val.externalApp.appName,
playStoreId: val.externalApp.playStoreId,
};
}
}
});
// WORKOUTS
contentOut.workouts = [];
content.workouts.map(async (val) => {
let workout = await MarsWorkout.findById(
val.id,
'name thumbnail video text required'
);
if (workout) {
contentOut.workouts.push({
id: val.id,
position: val.position,
name: workout.name,
panelImage: workout.thumbnail.split('/').slice(-1)[0],
programVideo: workout.video.split('/').slice(-1)[0],
instructions: workout.text,
required: workout.required,
});
}
});
// FILES
contentOut.files = [];
content.files.map(async (val) => {
let file = await Asset.findById(val, 'uri updated_at');
if (file) {
contentOut.files.push({
id: val,
uri: file.uri,
filename: file.uri.split('/').slice(-1)[0],
timestamp: file.updated_at,
});
}
});
return contentOut;
} else {
return {};
}
}
}
Here is the query I'm running in the Playground:
query {
getMarsContentForScreen(screen: "GS123123123123", token: "token-here") {
id
name
entry {
entryVideos
}
equipment {
id
name
position
panelImage
productImage
}
exercises {
equipment
content {
id
position
name
level
panelImage
programVideo
instructions
}
}
options {
bgImage
cards {
name
panelImage
subheading
action
overlay
externalApp {
appName
playStoreId
}
}
}
workouts {
id
position
name
panelImage
programVideo
required
instructions
}
files {
id
filename
uri
timestamp
}
}
}
And here is the output of what I'm getting:
{
"data": {
"getMarsContentForScreen": {
"id": "6203d63f54a0bd82832288c5",
"name": "sdfgsdfg",
"entry": [
{
"entryVideos": [
"6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"9b1628af-e69e-4d0e-9d53-b472a963a1ec.mp4",
"830b0258-70f1-4206-b07b-fb60508e33c5.mp4"
]
}
],
"equipment": [
{
"id": "62025aa4237005069c569d63",
"name": "dsfgsdfg",
"position": null,
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"productImage": "da245241-335e-4021-929c-d177a851c2ea.jpg"
},
{
"id": "62025afa237005069c569d99",
"name": "sdfgsdfgsdfgsdfgsdfgsdfgweqqwerwr",
"position": null,
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"productImage": "da245241-335e-4021-929c-d177a851c2ea.jpg"
},
{
"id": "62025af4237005069c569d92",
"name": "sdfgsdfgsdfgdsf",
"position": null,
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"productImage": "da245241-335e-4021-929c-d177a851c2ea.jpg"
}
],
"exercises": [
{
"equipment": "dsfgsdfg",
"content": [
{
"id": "62025b27237005069c569dc0",
"position": 1,
"name": "sdfgsdfg",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"sdfgsdfg",
"sdfgsdfg",
"sdfg"
]
},
{
"id": "62025b30237005069c569dc7",
"position": 2,
"name": "sdfgsdfgsdfg",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"sdfgsdfg",
"sdfg",
"hgfjgh"
]
}
]
},
{
"equipment": "sdfgsdfgsdfgdsf",
"content": [
{
"id": "62025b80237005069c569e13",
"position": 1,
"name": "sdfg",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"sdfg",
"sdfgsdfg",
"sdfgdf"
]
}
]
},
{
"equipment": "sdfgsdfgsdfgsdfgsdfgsdfgweqqwerwr",
"content": [
{
"id": "62025b88237005069c569e1a",
"position": 1,
"name": "uitytyui",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"ytuityui",
"tyui",
"tyuityuityui"
]
}
]
}
],
"options": [
{
"bgImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"cards": []
}
],
"workouts": [],
"files": []
}
}
}
As you can see, everything from "options" : [{"cards"}] is empty, but it shouldn't be, as there is the data in the database for it.
What is even more interesting, is that when I console.log the contentOut object inside the last .map function (content.files.map()) I'm getting the full response.
Basically it looks like my resolver is returning the content before all of it is gathered.
If I add some if statement to check if all of my content is in the contentOut object, I'm getting empty response, just like the resolver couldn't be bothered to wait for all of the content...
Any ideas?
Many thanks in advance!
Ok, so after more Googling and fighting with it, I've re-write the whole code and use Promise.all for each part of the function in order to make sure that it will wait for the outcome of each await, before returning the value.
Now the code looks like this:
getMarsContentForScreen: async (_, { screen, token }, context) => {
if (!context.screen) return {};
console.log(screen, token);
const contentOut = {};
const screenExist = await MarsScreen.findOne({
name: screen,
token: token,
});
const getEntryVideos = async (content) => {
let result = [{ entryVideos: [] }];
await Asset.find({ _id: { $in: content } }, 'uri').then((response) =>
response.map((val) => {
result[0].entryVideos.push(val.uri.split('/').slice(-1)[0]);
})
);
return result;
};
const getEquipment = async (content) => {
let result = [];
const ids = content.map((val) => {
return val.id;
});
await MarsEquipment.find(
{ _id: { $in: ids } },
'id name thumbnail background'
).then((response) =>
response.map((val) => {
result.push({
id: val.id,
name: val.name,
panelImage: val.thumbnail.split('/').slice(-1)[0],
productImage: val.background.split('/').slice(-1)[0],
});
})
);
return result;
};
const getExercises = async (content) => {
let result = [];
const ids = [].concat(
...content.map((val) => {
result.push({
equipment: val.equipment,
content: [],
});
return val.content.map((valC) => {
return valC.id;
});
})
);
await MarsExercise.find(
{ _id: { $in: ids } },
'id name level text thumbnail video product'
).then((response) =>
response.map((exer) => {
let instructions = [];
const index = result.indexOf(
result.find((equip) => equip.equipment === exer.product)
);
for (const [key, value] of Object.entries(JSON.parse(exer.text))) {
instructions.push(value);
}
result[index].content.push({
id: exer.id,
position: exer.position,
name: exer.name,
level: exer.level,
instructions: instructions,
panelImage: exer.thumbnail.split('/').slice(-1)[0],
programVideo: exer.video.split('/').slice(-1)[0],
});
})
);
return result;
};
const getOptions = async (content) => {
let result = content;
const ids = content[0].cards.map((val) => {
return val.panelImage;
});
await Asset.findById(content[0].bgImage, 'uri').then((response) => {
result[0].bgImage = response.uri.split('/').slice(-1)[0];
});
await Asset.find({ _id: { $in: ids } }, 'id uri').then((response) =>
response.map((val) => {
let index = result[0].cards.indexOf(
result[0].cards.find((card) => card.panelImage === val.id)
);
result[0].cards[index].panelImage = val.uri.split('/').slice(-1)[0];
})
);
return result;
};
const getWorkouts = async (content) => {
let result = content;
const ids = content.map((val) => {
return val.id;
});
await MarsWorkout.find(
{ _id: { $in: ids } },
'id name thumbnail video text required'
).then((response) => {
response.map((val) => {
let index = result.indexOf(
result.find((work) => work.id === val.id)
);
result[index].panelImage = val.thumbnail.split('/').slice(-1)[0];
result[index].programVideo = val.video.split('/').slice(-1)[0];
});
});
return result;
};
const getFiles = async (content) => {
let result = [];
await Asset.find({ _id: { $in: content } }, 'id uri updated_at').then(
(response) => {
response.map((val) => {
result.push({
id: val.id,
uri: val.uri,
filename: val.uri.split('/').slice(-1)[0],
timestamp: val.updated_at,
});
});
}
);
return result;
};
if (screenExist) {
const content = await MarsContent.findOne({
screens: { $in: screenExist.id },
});
if (content) {
// ID
contentOut.id = content.id;
// NAME
contentOut.name = content.name;
// ENTRY
const entry = getEntryVideos(content.entry);
// EQUIPMENT
const equipment = getEquipment(content.equipment);
// EXERCISES
const exercises = getExercises(content.exercises);
// OPTIONS
const options = getOptions(content.options);
// WORKOUTS
const workouts = getWorkouts(content.workouts);
// FILES
const files = getFiles(content.files);
// PROMISE
const results = await Promise.all([
entry,
equipment,
exercises,
options,
workouts,
files,
]);
//console.log(results);
return {
id: content.id,
name: content.name,
entry: results[0],
equipment: results[1],
exercises: results[2],
options: results[3],
workouts: results[4],
files: results[5],
};
} else {
return {};
}
}
},

Lambda - Querying DynamoDB through Lambda

I have a table called Customers with attributes CustId(partition Key), Fname, Lname, Dob.
I created a secondary index called LastNameIndex on Lname with the following params:
{
TableName: 'Customers'
AttributeDefinitions: [
{
AttributeName: 'Lname',
AttributeType: 'S'
}
],
GlobalSecondaryIndexUpdates: [
{
Create: {
IndexName: "LastNameIndex",
KeySchema: [
{AttributeName: "Lname", KeyType: "HASH"}
],
Projection: {
"ProjectionType": "ALL"
},
ProvisionedThroughput: {
"ReadCapacityUnits": 1,"WriteCapacityUnits": 1
}
}
}
]
}
Lambda function (snippet) - I want to get all records with Lname=Connors
params = {
TableName: "Customers",
IndexName: "LastNameIndex",
ExpressionAttributeNames: {
"#FN": "Fname",
"#LN": "Lname",
"#DB": "Dob",
},
ExpressionAttributeValues: {
":a": {
S: "Connors"
}
},
KeyConditionExpression: "Lname = :a",
ProjectionExpression: "#FN, #LN, #DB"
};
Running the query
ddb.query(params).promise().then(
function(data) {
console.log("Customer:" + data.Item)
return data.Item;
},
function() {
console.log("No records found...")
}
);
I have a record with Lname = Connors.
But the query does not return me any records - any idea what's wrong with the params?
The query operation returns multiple items not a single item like getItem operation. so I think returning data.Items instead of data.Item should work just fine.
ddb.query(params).promise().then(
function(data) {
console.log("Customer:" + data.Items)
return data.Items;
},
function() {
console.log("No records found...")
}
);

How to remove an object from a list in DynamoDB using Lambda

I'm trying to write a Lambda function to remove an object from a list in DynamoDB. In this case I am passing in "author": "J.K. Rowling", "title": "Harry Potter", and "userid": "041c9004" as parameters. I want to delete the matching object from the books list. What is the correct syntax for the UpdateExpression statement? There may be some other errors within params{} as well.
The DynamoDB table looks like this. userid is the primary key:
{
"books": [
{
"author": "J.R.R. Tolkien",
"title": "Lord of the Rings"
},
{
"author": "J.K Rowling",
"title": "Harry Potter"
},
{
"author": "George RR Martin",
"title": "A Song of Ice and Fire"
}
],
"isactive": true,
"ispublic": true,
"lastupdated": 1597690265,
"userid": "041c9004"
}
Here is the Lambda function:
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
exports.handler = function(event, context, callback){
let params = {
ExpressionAttributeValues: {
":attrValue": [{
"author": event.author,
"title": event.title
}]
},
ExpressionAttributeNames : {
"#books" : "books"
},
Key: {
userid: event.userid
},
TableName: 'Users',
UpdateExpression: "REMOVE #books[:attrValue]", //this is incorrect
ReturnValues:"ALL_NEW",
};
docClient.update(params, function(err,data){
if(err) {
callback(err, null)
}else{
callback(null, data)
}
});
}
This is a great question!
Unfortunately, the UpdateExpression will only allow you to REMOVE from the list if you specify an index (docs here).
You'll need to read the item from the DB, find the indexes you want to remove, and REMOVE that specific index.
In case anyone is wondering, here is the full solution:
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
exports.handler = function(event, context, callback){
var params = {
TableName: 'Users',
Key: {
userid: event.userid
}
};
docClient.get(params, function(err, data){
if(err) {
callback(err,null);
} else {
var indexOfAuthor = data.Item.books.findIndex(i => i.author === event.author);
console.log('The index of the author is ' + indexOfAuthor);
var updateExpressionString = "REMOVE #books[" + indexOfAuthor + "]"
let paramsdelete = {
ExpressionAttributeNames : {
"#books" : "books"
},
Key: {
userid: event.userid
},
TableName: 'Users',
UpdateExpression: updateExpressionString,
ReturnValues:"ALL_NEW",
};
docClient.update(paramsdelete, function(err,data){
if(err) {
callback(err, null);
}else{
callback(null, data);
}
});
}
});
};

node.js: DynamoDB DocumentClient returning empty object

I am using DynamoDB local and can create and delete table. I created a table with only one key like below
const tablePromise = dynamodb.listTables({})
.promise()
.then((data) => {
const exists = data.TableNames
.filter(name => {
return name === tableNameLogin;
})
.length > 0;
if (exists) {
return Promise.resolve();
}
else {
const params = {
TableName: tableNameLogin,
KeySchema: [
{ AttributeName: "email", KeyType: "HASH"}, //Partition key
],
AttributeDefinitions: [
{ AttributeName: "email", AttributeType: "S" },
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10
}
};
dynamodb.createTable(params, function(err, data){
if (err) {
console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2));
}
});
}
});
Now I want to insert an item in the table following example doc at AWS.
var docClient = new AWS.DynamoDB.DocumentClient();
var tableNameLogin = "Login"
var emailLogin = "abc#gmail.com";
var params = {
TableName:tableNameLogin,
Item:{
"email": emailLogin,
"info":{
"password": "08083928"
}
}
};
docClient.put(params, function(err, data) {
if (err) {
console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Added item:", JSON.stringify(data, null, 2));
}
});
When I run the insert item code, I get Added item: {} Why does it output an empty object? Is it actually inserting anything? I looked into this callback example but this time it doesn't output anything.
You need to add ReturnValues: 'ALL_OLD' to your put params. It will look like as mentioned below.
var params = {
TableName:tableNameLogin,
Item:{
"email": emailLogin,
"info":{
"password": "08083928"
}
},
ReturnValues: 'ALL_OLD'
};
For more details, you can follow this https://github.com/aws/aws-sdk-js/issues/803

Resources