使用 JavaScript 程式設計 Amazon DynamoDB - Amazon DynamoDB

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 JavaScript 程式設計 Amazon DynamoDB

本指南提供程式設計人員想要搭配 JavaScript 使用 Amazon DynamoDB 的方向。了解 AWS SDK for JavaScript、可用的抽象層、設定連線、處理錯誤、定義重試政策、管理保持連線等。

關於 AWS SDK for JavaScript

AWS SDK for JavaScript 提供 AWS 服務 使用瀏覽器指令碼或 Node.js 存取 。本文件著重於 SDK (V3) 的最新版本。V3 AWS SDK for JavaScript 由 維護 AWS 為 GitHub 上託管的開放原始碼專案。問題和功能請求是公開的,您可以在 GitHub 儲存庫的問題頁面上存取它們。

JavaScript V2 類似於 V3,但包含語法差異。V3 更模組化,可更輕鬆地提供較小的相依性,並支援一級 TypeScript。建議使用最新版本的 SDK。

使用 AWS SDK for JavaScript V3

您可以使用 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 模組語法。如需這兩種方法的進一步詳細資訊,請參閱考量一節。

存取 JavaScript 文件

使用下列資源開始使用 JavaScript 文件:

  • 存取核心 JavaScript 文件的開發人員指南。安裝指示位於設定區段。

  • 存取 API 參考文件,以探索所有可用的類別和方法。

  • 適用於 JavaScript 的 SDK 支援 DynamoDB AWS 服務 以外的許多 。使用下列程序來尋找 DynamoDB 的特定 API 涵蓋範圍:

    1. 服務中,選擇 DynamoDB 和程式庫。這會記錄低階用戶端。

    2. 選擇 lib-dynamodb。這會記錄高階用戶端。兩個用戶端代表您可以選擇使用的兩個不同的抽象層。如需抽象層的詳細資訊,請參閱以下章節。

抽象層

適用於 JavaScript V3 的 SDK 具有低階用戶端 (DynamoDBClient) 和高階用戶端 (DynamoDBDocumentClient)。

低階用戶端 (DynamoDBClient)

低階用戶端不會對基礎線路通訊協定提供額外的抽象。它可讓您完全控制通訊的各個層面,但由於沒有抽象,您必須使用 DynamoDB JSON 格式執行類似提供項目定義等動作。

如以下範例所示,此格式資料類型必須明確陳述。S 表示字串值,N 表示數值。線路上的數字一律會以標記為數字類型的字串傳送,以確保精確度不會遺失。低階 API 呼叫具有命名模式,例如 PutItemCommandGetItemCommand

下列範例使用低階用戶端搭配使用 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)

高階 DynamoDB 文件用戶端提供內建的便利功能,例如不需要手動封送資料,以及允許使用標準 JavaScript 物件直接讀取和寫入。的 文件lib-dynamodb提供優點清單。

若要執行個體化 DynamoDBDocumentClient,請建構低階 ,DynamoDBClient然後使用 包裝它DynamoDBDocumentClient。函數命名慣例在兩個套件之間略有不同。例如,低階 使用 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、 或 等 API 操作讀取項目時Query,使用模式是一致的Scan

使用 marshall 公用程式函數

您可以使用低階用戶端和 marshall,或自行取消marshall 資料類型。公用程式套件 util-dynamodb 具有接受 JSON 的marshall()公用程式函數,並產生 DynamoDB JSON,以及反向執行的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();

讀取項目

若要從 DynamoDB 讀取單一項目,請使用 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();

條件式寫入

DynamoDB 寫入操作可以指定邏輯條件表達式,必須評估為 true 才能繼續寫入。如果條件未評估為 true,則寫入操作會產生例外狀況。條件表達式可以檢查項目是否已存在,或其屬性是否符合特定限制條件。

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

當條件式表達式失敗時,您可以使用 ReturnValuesOnConditionCheckFailure請求錯誤回應包含不符合條件的項目,以推斷問題所在。如需詳細資訊,請參閱使用 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; } }

其他程式碼範例顯示 JavsScript SDK V3 使用的其他層面,可在 JavaScript SDK V3 文件DynamoDB-SDK-Examples GitHub 儲存庫下取得。

分頁

讀取請求,例如 ScanQuery 可能會傳回資料集中的多個項目。如果您Query使用 Limit 參數執行 Scan或 ,則一旦系統讀取了這麼多項目,就會傳送部分回應,而且您將需要分頁以擷取其他項目。

系統每個請求最多只會讀取 1 MB 的資料。如果您包含Filter表達式,系統仍會從磁碟讀取最多 1 MB 的資料,但會傳回符合篩選條件的 MB 項目。篩選操作可以傳回 0 個頁面的項目,但在搜尋用盡之前仍需要進一步分頁。

您應該在回應LastEvaluatedKey中尋找 ,並在後續請求中使用它做為 ExclusiveStartKey 參數,以繼續資料擷取。這可做為書籤,如下列範例所述。

注意

範例會在第一次反覆運算ExclusiveStartKey時將 null lastEvaluatedKey做為 傳遞,這是允許的。

使用 的範例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便利方法

開發套件提供稱為 paginateScan的便利方法paginateQuery,可為您執行此作業,並在幕後重複請求。使用標準Limit參數,指定每個請求要讀取的項目數量上限。

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); });
注意

除非資料表很小,否則定期執行完整資料表掃描不是建議的存取模式。

指定組態

設定 時DynamoDBClient,您可以透過將組態物件傳遞至建構函數來指定各種組態覆寫。例如,如果呼叫內容或要使用的端點 URL 尚不知道,您可以指定要連線的區域。如果您想要將 DynamoDB Local 執行個體設為目標以用於開發用途,這會很有用。

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

逾時的組態

DynamoDB 使用 HTTPS 進行用戶端-伺服器通訊。您可以透過提供NodeHttpHandler物件來控制 HTTP 層的某些層面。例如,您可以調整金鑰逾時值 connectionTimeoutrequestTimeoutconnectionTimeout 是用戶端在嘗試建立連線時,在放棄之前所等待的最大持續時間,以毫秒為單位。

requestTimeout 定義用戶端在傳送請求後等待回應的時間,也是以毫秒為單位。兩者的預設值都是零,表示逾時已停用,而且如果回應未到達,用戶端將等待的時間沒有限制。您應該將逾時設定為合理,以便在發生網路問題時,請求會發生錯誤,並可以啟動新的請求。例如:

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 匯入。Smithy 是一種語言,用於定義 服務和 SDKs、開放原始碼,並由 維護 AWS。

除了設定逾時值之外,您還可以設定通訊端的最大數量,這允許每個原始伺服器增加並行連線的數量。開發人員指南包含設定 maxSockets 參數的詳細資訊

保持連線的組態

使用 HTTPS 時,第一個請求一律需要back-and-forth通訊才能建立安全連線。HTTP Keep-Alive 允許後續請求重複使用已建立的連線,使請求更有效率並降低延遲。HTTP Keep-Alive 預設為使用 JavaScript V3 啟用。

閒置連線可保持運作的時間有限制。如果您有閒置連線,但希望下一個請求使用已建立的連線,請考慮每分鐘傳送定期請求。

注意

請注意,在 SDK 的較舊 V2 中,保持連線預設為關閉,這表示每個連線都會在使用後立即關閉。如果使用 V2,您可以覆寫此設定。

重試的組態

當軟體開發套件收到錯誤回應,且錯誤可由軟體開發套件決定可繼續,例如限流例外狀況或暫時服務例外狀況時,將會再次重試。以發起人身分,您看不見會發生這種情況,但您可能會注意到請求需要更長的時間才能成功。

根據預設,適用於 JavaScript V3 的 SDK 將提出總共 3 個請求,然後再放棄錯誤並將錯誤傳遞至呼叫內容。您可以調整這些重試次數和頻率。

DynamoDBClient 建構函數接受限制將發生多少次嘗試maxAttempts的設定。以下範例會將預設值 3 提高為總計 5。如果您將其設定為 0 或 1,表示您不想要任何自動重試,並想要自行在擷取區塊內處理任何可繼續的錯誤。

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

您也可以使用自訂重試策略來控制重試的時間。若要這樣做,請匯入util-retry公用程式套件,並建立自訂退避函數,根據目前的重試計數計算重試之間的等待時間。

以下範例指出,如果第一次嘗試失敗,最多嘗試 5 次,延遲為 15、30、90 和 360 毫秒。自訂退避函數 會接受重試嘗試次數 (以 1 開始,第一次重試) 來計算延遲 calculateRetryBackoff,並傳回等待該請求的毫秒數。

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. ), });

等待程式

DynamoDB 用戶端包含兩個有用的等待程式函數,可在您希望程式碼等待資料表修改完成時,在建立、修改或刪除資料表時使用。例如,您可以部署資料表、呼叫 waitUntilTableExists函數,程式碼會封鎖,直到資料表變成 ACTIVE 為止。等待程式會在內部輪詢 DynamoDB 服務,describe-table每 20 秒一次。

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 此功能只會在可以執行顯示資料表狀態 ACTIVEdescribe-table命令時傳回控制項。這可確保您可以使用 waitUntilTableExists等待建立完成,以及新增 GSI 索引等修改,在資料表返回 ACTIVE 狀態之前,可能需要一些時間才能套用。

錯誤處理

在這裡的早期範例中,我們廣泛地發現了所有錯誤。不過,在實際的應用程式中,請務必分辨各種錯誤類型,並實作更精確的錯誤處理。

DynamoDB 錯誤回應包含中繼資料,包括錯誤的名稱。您可以擷取錯誤,然後比對可能的錯誤條件字串名稱,以判斷如何繼續。對於伺服器端錯誤,您可以利用 instanceof運算子搭配@aws-sdk/client-dynamodb套件匯出的錯誤類型,有效率地管理錯誤處理。

請務必注意,這些錯誤只有在所有重試都用盡之後才會顯示。如果重試錯誤,且最終接續成功呼叫,從程式碼的角度來看,沒有錯誤只是延遲稍微增加。重試結果會在 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 時發生錯誤請參閱 。您可以在該 API 呼叫的文件中找到任何特定 API 呼叫的確切錯誤,例如查詢 API 文件

錯誤中繼資料包含其他屬性,視錯誤而定。對於 TimeoutError,中繼資料包含已嘗試的次數和 totalRetryDelay,如下所示。

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

如果您管理自己的重試政策,建議您區分調節和錯誤:

  • 調節 (由 ProvisionedThroughputExceededException或 表示ThrottlingException) 表示運作狀態良好的服務,通知您已超過 DynamoDB 資料表或分割區上的讀取或寫入容量。每經過一毫秒,就會提供多一點的讀取或寫入容量,因此您可以快速重試,例如每 50 毫秒,以嘗試存取該新發佈的容量。

    使用調節時,您不需要特別使用指數退避,因為調節很輕量,DynamoDB 可以傳回,而且不會向您收取每次請求的費用。指數退避會將較長的延遲指派給已等待最長時間的用戶端執行緒,這在統計上會將 p50 和 p99 向外延伸。

  • 錯誤 (由 InternalServerErrorServiceUnavailable等表示) 表示服務有暫時性問題,可能是整個資料表,也可能是您正在讀取或寫入的分割區。發生錯誤時,您可以在重試之前暫停更長的時間,例如 250 毫秒或 500 毫秒,並使用抖動來交錯重試。

日誌

開啟記錄功能,以取得 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 行為。如需詳細資訊,請參閱 部落格簡介模組化 Middleware Stack AWS SDK for JavaScript

考量事項

在專案 AWS SDK for JavaScript 中實作 時,以下是一些需要考慮的因素。

模組系統

SDK 支援兩個模組系統:CommonJS 和 ES (ECMAScript)。CommonJS 使用 require函數,而 ES 使用 import關鍵字。

  1. 常見 JSconst { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");

  2. ES (ECMAScriptimport { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";

專案類型會指定要使用的模組系統,並在 package.json 檔案的類型區段中指定。預設值為 CommonJS。使用 "type": "module" 來指示 ES 專案。如果您有使用 CommonJS 套件格式的現有 Node.JS 專案,您仍然可以透過使用 .mjs 副檔名命名函數檔案,來新增具有較現代 SDK V3 匯入語法的函數。這將允許將程式碼檔案視為 ES (ECMAScript)。

非同步操作

您會看到許多程式碼範例使用回呼,並承諾處理 DynamoDB 操作的結果。使用現代 JavaScript,不再需要這種複雜性,開發人員可以利用更簡潔且可讀取的非同步/等待語法進行非同步操作。

Web 瀏覽器執行時間

使用 React 或 React Native 建置的 Web 和行動開發人員可以在其專案中使用適用於 JavaScript 的 SDK。使用舊版 SDK 的 V2,Web 開發人員必須將完整的 SDK 載入瀏覽器,並參考託管在 https://https://sdk.amazonaws.com/js/ 的 SDK 映像。

使用 V3,您可以使用 Webpack 將所需的 V3 用戶端模組和所有必要的 JavaScript 函數綁定到單一 JavaScript 檔案中,並將其新增到 HTML 頁面 <head> 中的指令碼標籤中,如 SDK 文件的瀏覽器指令碼入門一節所述。

DAX 資料平面操作

適用於 JavaScript V3 的 SDK 目前不支援 Amazon DynamoDB Streams Accelerator (DAX) 資料平面操作。如果您請求 DAX 支援,請考慮使用支援 DAX 資料平面操作的適用於 JavaScript V2 的 SDK。