blog-image

node.jsでのdynamodb操作まとめ

2023-11-21

awsDynamoDB

node.jsでのdynamodb操作操作をする機会があったため、各種操作方法をまとめておきます。 環境は以下です。

  • node.js v18
  • aws sdk v3

公式サイト

想定するDynamoDBスキーマ

  • パーティションキー
    • AccountType
  • ソートキー
    • user_id
  • 属性
    • role_id

共通

DynamoDBClientのみで頑張って書いても大丈夫ですが型定義など書かないといけないのでめんどくさいです。 DocumentClientを使うとスッキリかけます。 詳しくは公式サイトを確認してください。

1import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";
2import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
3
4
5const marshallOptions = {
6    // Whether to automatically convert empty strings, blobs, and sets to `null`.
7    convertEmptyValues: false, // false, by default.
8    // Whether to remove undefined values while marshalling.
9    removeUndefinedValues: true, // false, by default.
10    // Whether to convert typeof object to map attribute.
11    convertClassInstanceToMap: false, // false, by default.
12};
13const translateConfig = { marshallOptions };
14const DynamoDBclient = new DynamoDBClient({
15    region: 'ap-northeast-1'
16});
17const dynamo = DynamoDBDocumentClient.from( DynamoDBclient, translateConfig );
18

検索系

GetCommand、QueryCommand、ScanCommandがあります。 ScanCommandはテーブルフルスキャンになり効率が悪い為あまりおすすめしませんのでここでは解説しません。 DynamoDBは検索条件がそこまで柔軟ではなく、テーブル設計時に検索条件を意識して作る必要があります。 パーティションキーは必ず検索条件に指定する必要があります。デフォルトの設定ではソートキーのみを検索条件にすることはできません。 パーティションキーには、完全一致条件のみ指定できます。

GetCommand

単一Itemの取得用 パーティションキーとソートキーどちらも設定されている場合はKeyにどちらの項目も必須です。 パーティションキーのみ設定されているテーブルの場合は、パーティションキーは必須です。

1const dynamo_data = await dynamo.send(
2    new GetCommand({
3        TableName: "tableName",
4        Key: {
5            AccountType: "ADMIN",
6            user_id: "1",
7        },
8    })
9);

QueryCommand

複数のItem取得用 パーティションキーとソートキーが設定されているテーブルの場合パーティションキーは必須項目になります。 呼び出しあたりのデータサイズ制限があり、1MBまでしか転送できません。

1const user_data = await dynamo.send(
2    new QueryCommand({
3        TableName: "tableName",
4        KeyConditionExpression: 'AccountType = :AccountType',
5        // プレースフォルダみたいなやつ
6        ExpressionAttributeValues: {
7            ":AccountType": "ADMIN",
8            ":role_id": "role_admin"
9        },
10        // 属性でフィルターかけたい時に使う。下記だとrole_idが"role_admin"で始まるものを検索
11        FilterExpression: "contains (#role_id, :role_id)",
12        // dynamodbの予約語に引っかかる時に使う
13        ExpressionAttributeNames: {
14            "#role_id": "role_id"
15        },
16        // 検索結果でuser_idしか返して欲しくない時とかに指定
17        // この指定がない場合はすべての項目の取得ができる
18        // データ転送の節約のため、指定することを推奨
19        ProjectionExpression: 'user_id',
20    })
21);

pagenetion

DynamoDBでは一度に取得できるデータ量が決まっているため、 QueryCommandなどで取得できない場合に使う sdk v2では もうちょっと冗長な書き方しかできなかった模様

1
2const marshallOptions = {
3    // Whether to automatically convert empty strings, blobs, and sets to `null`.
4    convertEmptyValues: false, // false, by default.
5    // Whether to remove undefined values while marshalling.
6    removeUndefinedValues: true, // false, by default.
7    // Whether to convert typeof object to map attribute.
8    convertClassInstanceToMap: false, // false, by default.
9};
10const translateConfig = { marshallOptions };
11const DynamoDBclient = new DynamoDBClient({
12    region: 'ap-northeast-1'
13});
14
15const dynamo = new DynamoDBDocumentClient(DynamoDBclient, translateConfig);
16const paginatorConfig = {
17    client: dynamo,
18    pageSize: 10,
19};
20const paginator = paginateQuery(paginatorConfig, {
21        TableName: "tableName",
22        KeyConditionExpression: 'AccountType = :AccountType',
23        ExpressionAttributeValues: {
24            ":AccountType": "ADMIN"
25        }
26    }
27);
28const items = [];
29for await (const page of paginator) {
30    items.push(...page.Items);
31}
32console.log(items);
33

追加、更新、削除

BatchWriteCommand

複数のItemの一括追加/更新/削除用 1回で最大25件まで処理できます。 書き込むすべての項目の合計サイズが 16MBを超えてはいけません。

1await dynamo.send(
2    new BatchWriteCommand({
3        RequestItems: {
4            "tableName": [
5                {
6                    PutRequest: {
7                        Item: {
8                            AccountType: "ADMIN",
9                            user_id: "4",
10                            role_id: "role_admin",
11                        },
12                    },
13                },
14                {
15                    DeleteRequest: {
16                        Key: {
17                            AccountType: "ADMIN",
18                            user_id: "1",
19                        },
20                    },
21                },
22            ],
23        },
24    })
25);

TransactWriteCommand

トランザクションで複数のItemの追加/更新/削除を行う際に使用します。 1回で最大100件まで処理できます。 トランザクションには、4MBを超えるデータを含めることはできません。

1await dynamo.send(
2    new TransactWriteCommand({
3        TransactItems: [
4            {
5                Put: {
6                    TableName: "tableName",
7                    Item: {
8                        AccountType: "ADMIN",
9                        user_id: "1",
10                        role_id: "role_admin",
11                    },
12                    ConditionExpression: "attribute_not_exists (user_id)",
13                },
14            },
15            {
16                Update: {
17                    TableName: "tableName",
18                    Key: {
19                        AccountType: "ADMIN",
20                        user_id: "1",
21                    },
22                    UpdateExpression: "set role_id = :role_id",
23                    ExpressionAttributeValues: {
24                        ":role_id": "role_admin",
25                    },
26                },
27            },
28            {
29                Delete: {
30                    TableName: "tableName",
31                    Key: {
32                        AccountType: "Admin",
33                        user_id: "3",
34                    },
35                },
36            },
37        ],
38    })
39);

UpdateCommand

上書き。元の値がない場合は追加になるので注意 UpdateExpressionのオプションが色々あるので、試してみると面白いです。

1await dynamo.send(
2    new UpdateCommand({
3        TableName: "tableName",
4        Key: {
5            AccountType: "ADMIN",
6            user_id: "1",
7        },
8        UpdateExpression: "set role_id = :role_id",
9        ExpressionAttributeValues: {
10            ":role_id": "role_admin_2",
11        },
12    })
13);

PutCommand

指定されたアイテムがテーブルにない場合は追加、ある場合は更新します。 PutCommandは、アイテムの完全な上書きを行うため、使用する前に必ず存在確認を行う必要があります。一方、UpdateCommandは、条件式を使用して更新を制御することができるため、既存のアイテムの一部だけを更新する場合に便利です。 パーティションキーとソートキーが設定されているテーブルの場合パーティションキーは必須項目になります。

1await dynamo.send(
2    new PutCommand({
3        TableName: "tableName",
4        Item: {
5            AccountType: "ADMIN",
6            user_id: "2",
7            role_id: "role_admin",
8        }
9    })
10);

DeleteCommand

削除コマンド。削除時に対象のItemがない場合もエラーにならない。

1await dynamo.send(
2    new DeleteCommand({
3        TableName: "tableName",
4        Key: {
5            AccountType: "ADMIN",
6            user_id: "1",
7        },
8        // DynamoDBは削除時に対象のItemがない場合もエラーにならない
9        // 対象のItemが存在しない場合エラーを出して欲しい時は下を指定する
10        ConditionExpression: "attribute_exists(AccountType) and attribute_exists(user_id)",
11    })
12);