Node.js (AWS SDK v3) での DynamoDB Query

AWS SDK v3 における完全な Query は、多くのスニペットが見せる単発の client.send() ではなく、以下の do/while です。1 ページは 1 MB で頭打ちになり、パーティションの残りは LastEvaluatedKey を戻して初めて届きます。そもそも Query が正しい読み取りなのかについては Query と Scan の比較を参照してください。

コード

import {DynamoDBClient, QueryCommand} from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});

const items = [];
let lastEvaluatedKey;

do {
  const response = await client.send(
    new QueryCommand({
      TableName: 'Music',
      KeyConditionExpression: '#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)',
      ExpressionAttributeNames: {
        '#hashKey': 'Artist',
        '#rangeKey': 'SongTitle'
      },
      ExpressionAttributeValues: {
        ':hashKeyValue': {S: 'Arturo Sandoval'},
        ':rangeKeyValue': {S: 'C'}
      },
      ExclusiveStartKey: lastEvaluatedKey
    })
  );

  items.push(...(response.Items ?? []));
  lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);

console.log(`Found ${items.length} items`);

このループが実際にやっていること

1 曲あたり約 3.9 KB の 600 曲がすべて Artist = "Arturo Sandoval" の下にあるフィクスチャに対して、上のループは 3 回リクエストを送ります。

往復CountScannedCount読み取りユニットLastEvaluatedKey
1271271128.5あり
2271271128.5あり
3585827.5なし

271 を設定した人は誰もいません。そこで 1 MB が尽きたというだけです。つまりページの境目は、アイテムサイズが変われば動きます。今日は 1 回でページングが終わるパーティションも、属性を 1 つ足せば 2 回になります。そして単発の send() から response.Items を読むコードは、600 曲のうち 271 曲を、エラーも出さずに静かに返します。

同じクエリに Limit: 10Year に対する FilterExpression を足してみましょう。

Count: 0   ScannedCount: 10   ConsumedCapacity: 5   LastEvaluatedKey: set

10 件を評価し、0 件を返し、それでもリクエストは読み取りキャパシティを消費しました。Limit が制限するのは DynamoDB が 読む 量で、フィルターはその後に走ります。したがって「結果を 10 件ください」という意味で選んだ Limit が返すのは 0〜10 件です。

2026-07-28 に、node v24.18.0 上の @aws-sdk/client-dynamodb 3.1095.0 で DynamoDB Local(amazon/dynamodb-local)に対して測定しました。件数とキャパシティはエンジン自身のレスポンスフィールドです。

解説

  • ExclusiveStartKey: lastEvaluatedKey は初回では undefined ですが、それは意図的です。v3 のシリアライザーは undefined のメンバーを落とすので、同じオブジェクトリテラルが最初のリクエストにも後続のすべてにも使えます。「先頭から始める」ときの当てずっぽうとして {} を入れると、ValidationException: The provided starting key is invalid で失敗します。
  • @aws-sdk/client-dynamodb は決してマーシャリングしてくれません。値は {S: 'Arturo Sandoval'} として入り、アイテムも同じ形で返ってきます。それが DocumentClient を引き込まないことの代償です。素の JS オブジェクトで書きたいなら、手を伸ばすラッパーは @aws-sdk/lib-dynamodb です。
  • 数値は往復を文字列のまま生き延びます@aws-sdk/util-dynamodbunmarshall{N: '9007199254740993'} をアンマーシャルすると、精度の落ちる number ではなく JS の bigint が返ります。{wrapNumbers: true} を渡せば代わりに {value: '9007199254740993'} になります。いずれにせよ、桁数を確認していない DynamoDB の NNumber() をかけてはいけません。
  • KeyConditionExpression はパーティションキーの等価条件に加えて、ソートキーの条件を最大 1 つ取ります(=<<=>>=BETWEENbegins_with)。それ以外は FilterExpression に属し、これは読み取りの後に走ります。
  • ScanIndexForward: false はソートキーの順序を反転します。デフォルトは昇順です。IndexName を足せば、同じコマンドがセカンダリインデックスに切り替わります。

ビジュアルに行う

DynamoDB クエリビルダーは、この形全体 — キー条件、名前と値のマップ、LastEvaluatedKey のループ — を実行可能な SDK v3 のプログラムとして出力するので、ページネーションが「うっかり忘れる部分」ではなくなります。

GUI で実際のテーブルに対してクエリを実行するには、キー条件のフォームとページング付きの結果グリッドを備えた DynoTable をダウンロードしてください。

関連ガイド

参考資料

このリクエストをビジュアルに組み立てる

この操作を無料の DynamoDB クエリビルダーで組み立て — キー条件、フィルタ、インデックス、Limit、ソート順、ページネーションループ — 実行可能な SDK v3・CLI・boto3 のプログラムとしてコピーして戻れます。

DynamoDB クエリビルダーを開く

Console なしで DynamoDB を扱う

DynamoDB では実行できない本物の SQL(JOINs、GROUP BY、集計)を実行する高速な DynamoDB デスクトップクライアント。ビジュアル編集と、あなた自身の Bedrock キーで動く AI エージェントを備えています。

30日間無料トライアル、クレジットカード不要 — その後は期限のない Free プラン。