2
1

More than 3 years have passed since last update.

node.js で DynamoDB に await put(or putitem)する時の正しい方法は?

Last updated at Posted at 2021-08-21

node.js でDynamoDBにawait putしエラーを捉える

検索して出てくるのがだいたい以下のコードでawaitした時のエラーの捉えかたの常套手段がわからなかったので書き留めておきます

    dynamodb.put(param, (err, data) => {
        if(err) {
            // エラー処理
        }
    })

パターン1、異常動作例

putが2回実行されてしまう駄目な例です
callbackを呼んでかつpromise()を呼んだ実行をawaitで待ちます、エラーを取る事ができて一見正常動作に見えます。
しかしCALLBACKが2回呼ばれてしまいます、結果的にはそれでも良い場合もあるとは思いますが2倍処理が実行される状況は正常動作とは言えないでしょう。

var aws = require('aws-sdk');
//var dynamodb = new aws.DynamoDB({region: 'ap-northeast-1'});// putItem は {"S": "hoge"} という構造体を要求されて面倒なので使わない
var dynamodb = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});// 型指定無しで put を使いたいのでこちらを採用

// 正常動作しない、putが2回実行されている
async function Write1() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };

    await dynamodb.put(param, (err, data) => {
        console.log("###CALLBACK"); // 2回呼ばれてしまう、おそらく後の promise のせい
        Err = err;
    }).promise(); // おそらく promise() のせいで CALLBACK が2回呼ばれている、がpromiseしないと await できない

    if(Err) {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    } else {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    }
}

async function main(event, context) {
    return Write1();
}

exports.handler = async (event, context) => {
    return await main(event, context);
};

パターン1に関するネット上の情報

stack overflowより

雑に解釈すると「非同期呼び出し解決のために競合する呼び出し方を複数持っている、そのためコールバック、promiseを同時に使うと2回実行されてしまう」という事みたいです。

以下、パターン2、パターン3で正常動作例を示します、パターン2が正解なんじゃないかと思います。

パターン2、正常動作例

前後のソースを省き関数部分のみとします、実験ソース全体は最後に記します。
コールバックを使わずにpromiseだけで解決します、多分これが正解だと思います

// 正常動作、promise
async function Write2() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };

    return await dynamodb.put(param).promise()
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}

パターン3、正常動作例

前後のソースを省き関数部分のみとします、実験ソース全体は最後に記します。
Promise内でコールバックを使います
パターン2をわざわざ焼き直したという感じでメリットを感じないですね、パターン2が正解なんだと思います。

// 正常動作、promise を作成する
async function Write3() {
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };

    return await new Promise((resolve, reject) => {
        dynamodb.put(param, (err, data) => {
            console.log("###CALLBACK");
            if(err) {
                reject(err);
            } else {
                resolve(true);
            }
        });
    })
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}

実験ソース全体


var aws = require('aws-sdk');
//var dynamodb = new aws.DynamoDB({region: 'ap-northeast-1'});// putItem は {"S": "hoge"} という構造体を要求されて面倒なので使わない
var dynamodb = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});// 型指定無しで put を使いたいのでこちらを採用

// 正常動作しない、恐らくputが2回実行されている
async function Write1() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };

    await dynamodb.put(param, (err, data) => {
        console.log("###CALLBACK"); // 2回呼ばれてしまう、おそらく後の promise のせい
        Err = err;
    }).promise(); // おそらく promise() のせいで CALLBACK が2回呼ばれている、がpromiseしないと await できない

    if(Err) {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    } else {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    }
}

// 正常動作、promise
async function Write2() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };

    return await dynamodb.put(param).promise()
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}

// 正常動作、promise を作成する
async function Write3() {
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };

    return await new Promise((resolve, reject) => {
        dynamodb.put(param, (err, data) => {
            console.log("###CALLBACK");
            if(err) {
                reject(err);
            } else {
                resolve(true);
            }
        });
    })
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}

async function main(event, context) {
    //return Write1();
    return Write2();
    //return Write3();
}

exports.handler = async (event, context) => {
    return await main(event, context);
};

if(!module.parent) {
    event = {
        "body-json": {}
    }

    new Promise(async () => {
        let contents = await main(event, null);
        console.log("###############main: contents", contents);
        return true;
    });
}

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1