

# JavaScript による Amazon DynamoDB のプログラミング
<a name="programming-with-javascript"></a>

このガイドは、JavaScript で Amazon DynamoDB を使用したいと考えているプログラマーを対象としています。AWS SDK for JavaScript、利用可能な抽象化レイヤー、接続の設定、エラー処理、再試行ポリシーの定義、キープアライブの管理などについて説明します。

**Topics**
+ [AWS SDK for JavaScript について](#programming-with-javascript-about)
+ [AWS SDK for JavaScript V3 を使用する](#programming-with-javascript-using-the-sdk)
+ [JavaScript のドキュメントを参照する](#programming-with-javascript-documentation)
+ [抽象化レイヤー](#programming-with-javascript-abstraction-layers)
+ [marshall ユーティリティ関数を使用する](#programming-with-javascript-using-marshall-utility)
+ [項目の読み込み](#programming-with-javascript-reading-items)
+ [条件付きの書き込み](#programming-with-javascript-conditional-writes)
+ [ページ分割](#programming-with-javascript-pagination)
+ [構成を指定する](#programming-with-javascript-config)
+ [ウェイター](#programming-with-javascript-waiters)
+ [エラー処理](#programming-with-javascript-error-handling)
+ [ログ記録](#programming-with-javascript-logging)
+ [考慮事項](#programming-with-javascript-considerations)

## AWS SDK for JavaScript について
<a name="programming-with-javascript-about"></a>

AWS SDK for JavaScript を使用すると、ブラウザスクリプトまたは Node.js のいずれかで AWS のサービス にアクセスできます。このドキュメントでは、最新バージョンの SDK (V3) を主に取り上げます。AWS SDK for JavaScript V3 は、AWS が管理する[オープンソースのプロジェクトであり、GitHub でホスト](https://github.com/aws/aws-sdk-js-v3)されています。Issue や Feature のリクエストは公開されており、GitHub リポジトリの [Issues] ページからアクセスできます。

JavaScript V2 は V3 と似ていますが、構文が違います。V3 の方がモジュール性が高いため、依存関係をより小さくして配布することが容易で、TypeScript のサポートも充実しています。最新バージョンの SDK を使用することをお勧めします。

## AWS SDK for JavaScript V3 を使用する
<a name="programming-with-javascript-using-the-sdk"></a>

Node Package Manager を使用して SDK を Node.js アプリケーションに追加できます。以下の例は、DynamoDB の操作に一般的に使われる SDK パッケージの追加方法を示しています。
+ `npm install @aws-sdk/client-dynamodb`
+ `npm install @aws-sdk/lib-dynamodb`
+ `npm install @aws-sdk/util-dynamodb`

パッケージをインストールすると、package.json プロジェクトファイルの依存関係セクションに参照が追加されます。新しい ECMAScript モジュール構文を使用することもできます。これら 2 つのアプローチの詳細については、「考慮事項」セクションを参照してください。

## JavaScript のドキュメントを参照する
<a name="programming-with-javascript-documentation"></a>

まずは、JavaScript のドキュメントを確認しましょう。以下のリソースを参照してください。
+ JavaScript の主要ドキュメントについては、「[デベロッパーガイド](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html)」を参照してください。インストール手順は「**Setting up**」セクションに記載されています。
+ [API リファレンス](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/)ドキュメントを参照し、使用可能なすべてのクラスとメソッドを確認してください。
+ SDK for JavaScript は DynamoDB 以外にも数多くの AWS のサービス をサポートしています。以下の手順にそって、DynamoDB の特定の API カバレッジを調べてください。

  1. **[Services]** から **[DynamoDB and Libraries]** を選択します。これは、低レベルクライアントをドキュメント化したものです。

  1. **lib-dynamodb** を選択します。これは、高レベルクライアントをドキュメント化したものです。これら 2 つのクライアントは、2 つの異なる抽象化レイヤーを表しています。任意で選んで使用できます。抽象レイヤーの詳細については、以下のセクションを参照してください。

## 抽象化レイヤー
<a name="programming-with-javascript-abstraction-layers"></a>

SDK for JavaScript V3 には、低レベルのクライアント (`DynamoDBClient`) と高レベルのクライアント (`DynamoDBDocumentClient`) があります。

**Topics**
+ [低レベルクライアント (`DynamoDBClient`)](#programming-with-javascript-low-level-client)
+ [高レベルクライアント (`DynamoDBDocumentClient`)](#programming-with-javascript-high-level-client)

### 低レベルクライアント (`DynamoDBClient`)
<a name="programming-with-javascript-low-level-client"></a>

低レベルクライアントでは、基盤のワイヤプロトコルが別段抽象化されません。通信をあらゆる側面から完全に制御できますが、抽象化されていないため、項目定義の提供などの操作は、DynamoDB JSON 形式を使って行う必要があります。

以下の例に示すように、この形式ではデータ型を明示的に指定する必要があります。*S* は文字列値を示し、*N* は数値を示します。ネットワーク上の数値は、精度が損なわれないように、必ず数値型としてタグ付けされた文字列として送信されます。低レベルの API コールには、`PutItemCommand` や `GetItemCommand` などの命名規則があります。

次の例では、低レベルクライアントを使用し、DynamoDB JSON で `Item` を定義しています。

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      "id": { S: "Product01" },
      "description": { S: "Hiking Boots" },
      "category": { S: "footwear" },
      "sku": { S: "hiking-sku-01" },
      "size": { N: "9" }
    }
  };

  try {
    const data = await client.send(new PutItemCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

### 高レベルクライアント (`DynamoDBDocumentClient`)
<a name="programming-with-javascript-high-level-client"></a>

高レベルの DynamoDB ドキュメントクライアントには、データを手動でマーシャリングする必要がない、標準の JavaScript オブジェクトを使用して直接読み書きできるなど、便利な機能が組み込まれています。[`lib-dynamodb` のドキュメント](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-lib-dynamodb/)には、利点が一覧で紹介されています。

`DynamoDBDocumentClient` をインスタンス化するには、まず、低レベルの `DynamoDBClient` を構築し、それを `DynamoDBDocumentClient` でラップします。関数の命名規則は 2 つのパッケージ間で若干異なります。例えば、低レベルでは `PutItemCommand` を使用し、高レベルでは `PutCommand` を使用します。名前が違えば、両方の関数セットが同じコンテキストで共存可能です。つまり、同一スクリプト内で両方を組み合わせることができます。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    },
  };

  try {
    const data = await docClient.send(new PutCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

addProduct();
```

`GetItem`、`Query`、`Scan` などの API オペレーションを使用して項目を読み取る場合の使用パターンは同じです。

## marshall ユーティリティ関数を使用する
<a name="programming-with-javascript-using-marshall-utility"></a>

低レベルクライアントを使用して、データ型を自分でマーシャリングまたはアンマーシャリングできます。ユーティリティパッケージ [util-dynamodb](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-util-dynamodb/) には、JSON を受け入れて DynamoDB JSON を生成する `marshall()` ユーティリティ関数と、その逆を行う `unmarshall()` 関数があります。次の例では、低レベルクライアントを使用し、`marshall()` を呼び出してデータマーシャリングを処理しています。

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");
const { marshall } = require("@aws-sdk/util-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: marshall({
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    }),
  };

  try {
    const data = await client.send(new PutItemCommand(params));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

## 項目の読み込み
<a name="programming-with-javascript-reading-items"></a>

DynamoDB から項目を 1 つ読み込むには、`GetItem` API オペレーションを使用します。`PutItem` コマンドと同様に、低レベルのクライアントまたは高レベルのドキュメントクライアントのいずれかを選んで使用できます。以下の例は、高レベルのドキュメントクライアントを使用して項目を取得する方法を示しています。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function getProduct() {
  const params = {
    TableName: "products",
    Key: {
      id: "Product01",
    },
  };

  try {
    const data = await docClient.send(new GetCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

getProduct();
```

`Query` API オペレーションを使用して、複数の項目を読み込みます。低レベルのクライアントまたはドキュメントクライアントを使用できます。以下の例では、高レベルのドキュメントクライアントを使用しています。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const {
  DynamoDBDocumentClient,
  QueryCommand,
} = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function productSearch() {
  const params = {
    TableName: "products",
    IndexName: "GSI1",
    KeyConditionExpression: "#category = :category and begins_with(#sku, :sku)",
    ExpressionAttributeNames: {
      "#category": "category",
      "#sku": "sku",
    },
    ExpressionAttributeValues: {
      ":category": "footwear",
      ":sku": "hiking",
    },
  };

  try {
    const data = await docClient.send(new QueryCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

productSearch();
```

## 条件付きの書き込み
<a name="programming-with-javascript-conditional-writes"></a>

DynamoDB の書き込みオペレーションでは、論理条件式を指定できます。条件式が true と評価されないと、書き込みは続行されません。条件の評価が true にならなかった場合は、例外が生成されます。条件式では、該当する項目が既に存在するかどうかや、その属性が特定の制約に一致するかどうかを確認できます。

`ConditionExpression = "version = :ver AND size(VideoClip) < :maxsize" `

条件式が失敗した場合は、`ReturnValuesOnConditionCheckFailure` を使用して、条件を満たさなかった項目をエラーレスポンスに含めるようにリクエストできるため、問題の原因究明に役立ちます。詳細については、「[Handle conditional write errors in high concurrency scenarios with Amazon DynamoDB](https://aws.amazon.com/blogs/database/handle-conditional-write-errors-in-high-concurrency-scenarios-with-amazon-dynamodb/)」を参照してください。

```
try {
      const response = await client.send(new PutCommand({
          TableName: "YourTableName",
          Item: item,
          ConditionExpression: "attribute_not_exists(pk)",
          ReturnValuesOnConditionCheckFailure: "ALL_OLD"
      }));
  } catch (e) {
      if (e.name === 'ConditionalCheckFailedException') {
          console.log('Item already exists:', e.Item);
      } else {
          throw e;
      }
  }
```

JavaScript SDK V3 のその他の使用法を示す追加のコード例は、[JavaScript SDK V3 ドキュメント](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_dynamodb_code_examples.html)と [DynamoDB-SDK-Examples GitHub リポジトリ](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/node.js)でご覧ください。

## ページ分割
<a name="programming-with-javascript-pagination"></a>

**Topics**
+ [便利な `paginateScan` メソッドを使用する](#using-the-paginatescan-convenience-method)

`Scan` や `Query` などの読み取りリクエストでは、データセット内の複数の項目が返される場合があります。`Limit` パラメータを指定して `Scan` や `Query` を実行した場合は、システムで多数の項目が全部読み取られた後、一部のレスポンスが送信されます。追加の項目を取得するには、ページ分割が必要です。

システムは、1 回のリクエストにつき最大 1 MB のデータのみを読み取ります。`Filter` 式を含めた場合も、システムは最大 1 MB のデータをディスクから読み取りますが、その 1 MB の中から、指定したフィルターに一致した項目だけを返します。フィルターオペレーションでは 1 ページあたりに返される項目数が 0 個になる場合もありますが、それでも、さらにページ分割をしないと、最後まで検索できません。

データの取得を継続するには、レスポンスで `LastEvaluatedKey` を探し、それを後続のリクエストの `ExclusiveStartKey` パラメータに指定する必要があります。これが、以下の例に示すように、ブックマークの役割を果たします。

**注記**  
この例では、1 回目の反復で null `lastEvaluatedKey` が `ExclusiveStartKey` として渡されています。これは許容されています。

`LastEvaluatedKey` を使用する例:

```
const { DynamoDBClient, ScanCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScan() {
  let lastEvaluatedKey;
  let pageCount = 0;

  do {
    const params = {
      TableName: "products",
      ExclusiveStartKey: lastEvaluatedKey,
    };

    const response = await client.send(new ScanCommand(params));
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, response.Items);
    lastEvaluatedKey = response.LastEvaluatedKey;
  } while (lastEvaluatedKey);
}

paginatedScan().catch((err) => {
  console.error(err);
});
```

### 便利な `paginateScan` メソッドを使用する
<a name="using-the-paginatescan-convenience-method"></a>



SDK には、`paginateScan` と `paginateQuery` という便利なメソッドがあります。これらは、ページ分割を自動で行い、繰り返しのリクエストをバックグラウンドで処理してくれます。標準の `Limit` パラメータを使用して、1 回のリクエストで読み取る項目の最大数を指定します。

```
const { DynamoDBClient, paginateScan } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScanUsingPaginator() {
  const params = {
    TableName: "products",
    Limit: 100
  };

  const paginator = paginateScan({client}, params);

  let pageCount = 0;

  for await (const page of paginator) {
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, page.Items);
  }
}

paginatedScanUsingPaginator().catch((err) => {
  console.error(err);
});
```

**注記**  
テーブルが小さくない限り、テーブル全体のスキャンを定期的に実行することは、アクセスパターンとして推奨されません。

## 構成を指定する
<a name="programming-with-javascript-config"></a>

**Topics**
+ [タイムアウトの設定](#programming-with-javascript-config-timeouts)
+ [キープアライブの設定](#programming-with-javascript-config-keep-alive)
+ [再試行の設定](#programming-with-javascript-config-retries)

`DynamoDBClient` を設定する際に、構成オブジェクトをコンストラクタに渡すことで、さまざまな構成オーバーライドを指定できます。例えば、接続先のリージョン (呼び出し側のコンテキストが把握していない場合) や、使用するエンドポイント URL を指定できます。開発目的で DynamoDB Local インスタンスをターゲットにする場合に便利です。

```
const client = new DynamoDBClient({
  region: "eu-west-1",
  endpoint: "http://localhost:8000",
});
```

### タイムアウトの設定
<a name="programming-with-javascript-config-timeouts"></a>

DynamoDB では、クライアント/サーバー通信に HTTPS を使用します。HTTP レイヤーの一部の側面は、`NodeHttpHandler` オブジェクトを指定することで制御できます。例えば、主要なタイムアウト値である `connectionTimeout` や `requestTimeout` を調整できます。`connectionTimeout` は、クライアントが接続を試みる際の最長待機時間 (ミリ秒単位) です。この時間内に確立できない場合は、接続を断念します。

`requestTimeout` では、リクエストが送信されてからクライアントがレスポンスを待機する時間 (ミリ秒単位) を定義します。いずれもデフォルト値は 0 です。その場合、タイムアウトは無効になり、レスポンスが届かなければクライアントは制限なく待ち続けることになります。ネットワークに問題が発生した場合にリクエストがエラーになり、新しいリクエストを開始できるように、妥当なタイムアウト値を設定しておいた方が賢明です。例えば、次のようになります。

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { NodeHttpHandler } from "@smithy/node-http-handler";

const requestHandler = new NodeHttpHandler({
  connectionTimeout: 2000,
  requestTimeout: 2000,
});

const client = new DynamoDBClient({
  requestHandler
});
```

**注記**  
この例では [Smithy](https://smithy.io/2.0/index.html) をインポートしています。Smithy はサービスと SDK を定義するための言語で、AWS が管理し、オープンソースで公開しています。

タイムアウト値を設定するほかに、最大ソケット数を設定し、オリジンあたりの同時接続数を増やすことができます。デベロッパーガイドでは、[`maxSockets` パラメータの設定](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html)について詳しく解説しています。

### キープアライブの設定
<a name="programming-with-javascript-config-keep-alive"></a>

HTTPS を使用する場合、最初のリクエストでは、安全な接続を確立するために何往復かの通信が必要になります。HTTP キープアライブを使用すると、すでに確立された接続を後続のリクエストで再利用できるため、リクエストの効率が上がり、レイテンシーが短縮されます。JavaScript V3 では、HTTP キープアライブがデフォルトで有効になっています。

アイドル状態の接続を維持できる時間には制限があります。接続がアイドル状態になるが、すでに確立されている接続を次のリクエストで利用したい場合は、定期的に (たとえば 1 分ごとに) リクエストを送信することを検討してください。

**注記**  
SDK の旧バージョン V2 では、キープアライブはデフォルトで無効になっているため、各接続は使用後すぐに切断されます。V2 を使用している場合は、この設定をオーバーライドできます。

### 再試行の設定
<a name="programming-with-javascript-config-retries"></a>

SDK がエラーレスポンスを受信し、そのエラーを SDK が再開可能であると判断した場合 (スロットリング例外や一時的なサービス例外など)、再試行されます。呼び出し側にはわからない形で行われますが、リクエスト成功までの時間が長引くという症状は現れます。

SDK for JavaScript V3 は、デフォルトではリクエストを合計 3 回行います。それで成功しなければそれ以上は再試行せず、呼び出し側のコンテキストにエラーを渡します。こうした再試行の回数と頻度を調整できます。

`DynamoDBClient` コンストラクタには、試行回数を制限する `maxAttempts` を設定できます。以下の例では、その値がデフォルトの 3 から合計 5 に引き上げられています。0 または 1 に設定した場合、自動再試行は不要であり、再開可能なエラーはキャッチブロック内で手動で処理するという意味になります。

```
const client = new DynamoDBClient({
  maxAttempts: 5,
});
```

また、再試行のタイミングも、カスタムの再試行戦略で制御できます。その場合は、`util-retry` ユーティリティパッケージをインポートし、現在の再試行が何回目かに応じて再試行の間隔を計算するカスタムのバックオフ関数を作成します。

以下の例では、1 回目の試行が失敗した場合は、15、30、90、360 ミリ秒と徐々に遅延を増やしながら最大 5 回試行するように指定しています。カスタムのバックオフ関数 ` calculateRetryBackoff` は、再試行回数 (初回の再試行が 1 で、以降増分) に応じて遅延を計算し、該当するリクエストで待機するミリ秒数を返します。

```
const { ConfiguredRetryStrategy } = require("@aws-sdk/util-retry");

const calculateRetryBackoff = (attempt) => {
  const backoffTimes = [15, 30, 90, 360];
  return backoffTimes[attempt - 1] || 0;
};

const client = new DynamoDBClient({
  retryStrategy: new ConfiguredRetryStrategy(
    5, // max attempts.
    calculateRetryBackoff // backoff function.
  ),
});
```

## ウェイター
<a name="programming-with-javascript-waiters"></a>

DynamoDB クライアントには、便利な[ウェーター関数](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/wait/index.html#cli-aws-dynamodb-wait)が 2 つあります。テーブルの作成、変更、削除時に、テーブルの変更が完了するまでコードの処理を待機させたい場合に、これらの関数を使用できます。例えば、テーブルをデプロイして `waitUntilTableExists` 関数を呼び出すと、テーブルが **ACTIVE** になるまでコードがブロックされます。ウェーターは内部的に 20 秒ごとに `describe-table` を実行し、DynamoDB サービスをポーリングします。

```
import {waitUntilTableExists, waitUntilTableNotExists} from "@aws-sdk/client-dynamodb";

… <create table details>

const results = await waitUntilTableExists({client: client, maxWaitTime: 180}, {TableName: "products"});
if (results.state == 'SUCCESS') {
  return results.reason.Table
}
console.error(`${results.state} ${results.reason}`);
```

`waitUntilTableExists` 関数は、`describe-table` コマンドを実行でき、テーブルのステータスが **ACTIVE** と表示された場合にのみ制御を返します。そのため、`waitUntilTableExists` を使用して、テーブルの作成や、GSI インデックスの追加などの変更の完了を待つことができます。こうした変更には時間がかかり、終わってからテーブルが **ACTIVE** ステータスに戻ります。

## エラー処理
<a name="programming-with-javascript-error-handling"></a>

ここで最初に紹介した数例では、すべてのエラーを網羅してキャッチしています。しかし、実際のアプリケーションでは、さまざまなエラータイプを見分け、より緻密なエラー処理を実装することが重要です。

DynamoDB エラーレスポンスには、エラーの名前を含むメタデータが含まれています。エラーをキャッチし、エラー条件の文字列名候補と照合して、処理方法を決定できます。サーバー側のエラーについては、`@aws-sdk/client-dynamodb` パッケージによってエクスポートされたエラータイプで `instanceof` 演算子を利用して、エラー処理を効率的に管理できます。

こうしたエラーは、再試行回数をすべて使い切って初めて露呈する点に注意が重要です。エラーが再試行され、最終的に呼び出しが成功した場合、コードの観点ではエラーは発生せず、レイテンシーが若干長引いただけになります。再試行は、スロットルリクエストやエラーリクエストなど、失敗したリクエストとして Amazon CloudWatch のグラフに表示されます。クライアントは再試行回数が最大回数に達すると、例外を生成します。つまり、クライアントはそれ以上再試行しないということがわかります。

以下のスニペットは、エラーをキャッチし、返されたエラーのタイプに基づいてアクションを実行します。

```
import {
  ResourceNotFoundException
  ProvisionedThroughputExceededException,
  DynamoDBServiceException,
} from "@aws-sdk/client-dynamodb";

try {
  await client.send(someCommand);
} catch (e) {
    if (e instanceof ResourceNotFoundException) {
      // Handle ResourceNotFoundException
    } else if (e instanceof ProvisionedThroughputExceededException) {
      // Handle ProvisionedThroughputExceededException
    } else if (e instanceof DynamoDBServiceException) {
      // Handle DynamoDBServiceException
    } else {
      // Other errors such as those from the SDK
      if (e.name === "TimeoutError") {
        // Handle SDK TimeoutError.
      } else {
        // Handle other errors.
      }
    }
}
```

一般的なエラー文字列については、「DynamoDB デベロッパーガイド**」の「[DynamoDB でのエラー処理](Programming.Errors.md)」を参照してください。特定の API コールで発生する可能性のある具体的なエラーについては、[Query API のドキュメント](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html)など、該当する API コールのドキュメントに記載されています。

エラーのメタデータには、エラーによって異なりますが、追加のプロパティが含まれます。` TimeoutError` の場合、以下に示すように、試行回数と `totalRetryDelay` がメタデータに含まれます。

```
{
  "name": "TimeoutError",
  "$metadata": {
    "attempts": 3,
    "totalRetryDelay": 199
  }
}
```

独自の再試行ポリシーを管理する場合は、スロットルとエラーを区別しておきましょう。
+ **スロットル** (` ProvisionedThroughputExceededException` または `ThrottlingException` で示される) は、DynamoDB テーブルまたはパーティションの読み取り容量または書き込み容量を超過したことを通知する正常なサービスを示します。1 ミリ秒が経過するごとに、読み取りまたは書き込みの容量が少しずつ増えるため、すぐに (50 ミリ秒ごとなど) 再試行して、新しく解放された容量へのアクセスを試みることができます。

   スロットルについては、エクスポネンシャルバックオフは特に必要ありません。軽量で DynamoDB が返しやすく、リクエストごとの課金も発生しないためです。エクスポネンシャルバックオフでは、すでに最長の待機時間が過ぎたクライアントスレッドの遅延をさらに長くしていくため、統計的に p50 と p99 が外側に広がります。
+ **エラー** (` InternalServerError` や `ServiceUnavailable` などで示される) が発生した場合は、サービス (テーブル全体、または読み取り/書き込み先のパーティションなど) に一時的な問題があります。エラーについては、再試行する前に比較的長く (250 ミリ秒や 500 ミリ秒など) 一時停止したり、ジッターを追加して再試行をずらしたりできます。

## ログ記録
<a name="programming-with-javascript-logging"></a>

ログ記録を有効にすると、SDK による処理内容を詳しく把握できます。以下の例に示すように、`DynamoDBClient` にパラメータを設定できます。ステータスコードや消費容量などのメタデータを含む詳細なログ情報がコンソールに表示されます。コードをターミナルウィンドウでローカルに実行した場合は、そのウィンドウにログが表示されます。AWS Lambda でコードを実行した場合、Amazon CloudWatch Logs が設定されていれば、コンソール出力がそのログに書き込まれます。

```
const client = new DynamoDBClient({
  logger: console
});
```

また、内部 SDK アクティビティにフックして、特定のイベントが発生したときにカスタムのログ記録を実行することもできます。以下の例では、クライアントの `middlewareStack` を使用して、各リクエストを SDK から送信された時点でインターセプトし、発生時にログに記録します。

```
const client = new DynamoDBClient({});

client.middlewareStack.add(
  (next) => async (args) => {
    console.log("Sending request from AWS SDK", { request: args.request });
    return next(args);
  },
  {
    step: "build",
    name: "log-ddb-calls",
  }
);
```

`MiddlewareStack` は、SDK の動作を観察して制御するための強力なフックを提供します。詳細については、ブログ「[Introducing Middleware Stack in Modular AWS SDK for JavaScript](https://aws.amazon.com/blogs/developer/middleware-stack-modular-aws-sdk-js/)」を参照してください。

## 考慮事項
<a name="programming-with-javascript-considerations"></a>

プロジェクトに AWS SDK for JavaScript を実装する際には、さらに以下の点を考慮する必要があります。

**モジュールシステム**  
SDK は CommonJS と ES (ECMAScript) の 2 つのモジュールシステムに対応しています。CommonJS は `require` 関数を使用し、ES は `import` キーワードを使用します。  

1. **CommonJS** – `const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");`

1. **ES (ECMAScript** – `import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";`
プロジェクトタイプは package.json ファイルの type セクションで指定され、使用するモジュールシステムを決定します。デフォルトは CommonJS です。`"type": "module"` を使用して、ES プロジェクトを指定します。CommonJS パッケージ形式を使用する既存の Node.JS プロジェクトがある場合でも、関数ファイルに .mjs 拡張子の付いた名前を指定することで、より新しい SDK V3 Import 構文を使用して関数を追加できます。そうすることで、コードファイルを ES (ECMAScript) として扱うことができます。

**非同期オペレーション**  
コールバックとプロミスを使用して DynamoDB オペレーションの結果を処理するサンプルコードが多数あります。最近の JavaScript では、そのように複雑な処理は必要なく、デベロッパーはもっと簡潔で読みやすい async/await 構文を活用して非同期オペレーションを実行できます。

**ウェブブラウザランタイム**  
React または React Native を使用してビルドしているウェブ開発者やモバイル開発者は、各自のプロジェクトで SDK for JavaScript を使用できます。SDK の旧バージョン V2 では、ウェブ開発者は https://sdk.amazonaws.com/js/ でホストされている SDK イメージを参照して、SDK 全体をブラウザにロードする必要がありました。  
V3 では、必要な V3 クライアントモジュールと必要なすべての JavaScript 関数を Webpack を使用して単一の JavaScript ファイルにバンドルし、HTML ページの `<head>` のスクリプトタグに追加できます。詳しくは、SDK ドキュメントの「[Getting started in a browser script](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started-browser.html)」セクションで説明しています。

**DAX データプレーンオペレーション**  
Amazon DynamoDB Streams Accelerator (DAX) データプレーンオペレーションは、SDK for JavaScript V3 でサポートされています。