Store JSON request in DynamoDB with Lambda

Viewed 1557

I have an http request in my Lambda function that returns a json. I'm using node.js. I want to save that json as an object in my DynamoDB table.

const http = require('https')
const AWS = require('aws-sdk');

AWS.config.update({region: "eu-west-1"});

exports.handler = async (event) => {
    return httprequest().then((data) => {
        //Parse JSON to objects

        // in this case not neccessary because it's already parsed.
        // data is already object

        // var arr_from_json = JSON.parse(data);

        //add Item to DynamoDB
        var params = {
            TableName: 'json',
            Item: {
                date: Date.now(),
                answer: data.answer,
                forced: data.forced,
                image: data.image
            }
        };

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

        docClient.put(params, function(err, data) {
            if (err) console.log(err);
            else console.log(data);
        });

        // Respond with JSON
        const response = {

            statusCode: 200,
            body: JSON.stringify(data),
        };
        return response;
    });
};

function httprequest() {
    return new Promise((resolve, reject) => {
        const options = {
            host: 'yesno.wtf',
            path: '/api',
            port: 443,
            method: 'GET'
        };
        const req = http.request(options, (res) => {
            if (res.statusCode < 200 || res.statusCode >= 300) {
                return reject(new Error('statusCode=' + res.statusCode));
            }
            var body = [];
            res.on('data', function (chunk) {
                body.push(chunk);
            });
            res.on('end', function () {
                try {
                    body = JSON.parse(Buffer.concat(body).toString());
                } catch (e) {
                    reject(e);
                }
                resolve(body);
            });
        });
        req.on('error', (e) => {
            reject(e.message);
        });
        // send the request
        req.end();
    });
}

JSON

{"answer":"no","forced":false,"image":"https://yesno.wtf/assets/no/8-5e08abbe5aacd2cf531948145b787e9a.gif"}

However, it's not storing the object in DynamoDB. What can I do differently? Is the section for storing in DynamoDB correct in the first place?

1 Answers

First, I assume that the table exists and that the lambda has the right permissions. Otherwise you would be getting a Requested resource not found or not authorized to perform: dynamodb:PutItem error, respectively.

You are calling docClient.put(...) but then just return a response from the lambda without waiting for DynamoDB's response. If you wait for DynamoDB's response before sending a response back, it works as expected, as you can see in the example below using await:

const AWS = require('aws-sdk');

// There's no need to re-instantiate this on each request:
const docClient = new AWS.DynamoDB.DocumentClient({ region: 'eu-west-1', apiVersion: 'latest' });    

function httprequest() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({
                answer: 'no',
                forced: false,
                image: 'https://yesno.wtf/assets/no/8-5e08abbe5aacd2cf531948145b787e9a.gif',
            });
        }, 2000);
    })
}

exports.handler = async (event) => {
    try {
        const data = await httprequest();

        const params = {
            Item: {
                date: Date.now(),
                answer: data.answer,
                forced: data.forced,
                image: data.image
            },

            TableName: 'json'
        };

        await docClient.put(params).promise();

        console.log('Document inserted.');

        return JSON.stringify(data);
    } catch(err) {
        console.log(err);

        return err;
    }
};

That being said, intuitively I'd say if docClient.put(...) is reached then the document should get inserted into DynamoDB anyway, but it looks like that's not the case, at least not when using a callback. However, if you replace that with docClient.put(params).promise(), the document will be inserted, even if you are not waiting for DynamoDB's response. docClient.put(params) alone won't work either.

I think that's a bit confusing/counter-intuitive, so I have created this other question to find out more about why that works the way it works:

Why values are not inserted into DynamoDB unless calling `.promise()`?

Related