AWS SDK for JS のタイムアウト設定に利用していた @aws-sdk/node-http-handler が deprecated となったため @smithy/node-http-handler で置き換えてみた

2023.10.31

こんにちは、CX 事業本部 Delivery 部の若槻です。

今回は、AWS SDK for JS のタイムアウト設定に利用していた @aws-sdk/node-http-handler が deprecated となったため @smithy/node-http-handler で置き換えてみました。

@aws-sdk/node-http-handler で型エラーが発生するようになった

@aws-sdk/node-http-handler では HTTP リクエストハンドラーが提供されています。これを利用することにより、下記で紹介されているように AWS SDK で HTTP リクエストのタイムアウトを設定することができます。

この @aws-sdk/node-http-handler ですが、ライブラリバージョンを次のようにアップデートしました。

package.json

{
  "dependencies": {
    "@aws-sdk/client-dynamodb": "3.438.0",
    "@aws-sdk/node-http-handler": "3.374.0"
  }
}

すると次のような TypeScript の型エラーが発生するようになりました。

Argument of type '[{ region: string; apiVersion: string; requestHandler: NodeHttpHandler; }]' is not assignable to parameter of type '[DynamoDBClientConfig] | []'.
  Type '[{ region: string; apiVersion: string; requestHandler: NodeHttpHandler; }]' is not assignable to type '[DynamoDBClientConfig]'.
    Type '{ region: string; apiVersion: string; requestHandler: NodeHttpHandler; }' is not assignable to type 'DynamoDBClientConfig'.
      Type '{ region: string; apiVersion: string; requestHandler: NodeHttpHandler; }' is not assignable to type 'ClientDefaults'.
        Types of property 'requestHandler' are incompatible.
          Type 'NodeHttpHandler' is not assignable to type 'HttpHandler'.
            Type 'NodeHttpHandler' is missing the following properties from type '{ updateHttpClientConfig(key: never, value: never): void; httpHandlerConfigs(): {}; }': updateHttpClientConfig, httpHandlerConfigsts(2345)

@aws-sdk/node-http-handler が deprecated となり、@smithy/node-http-handler への移行が推奨されていた

@aws-sdk/node-http-handler の npm ページを見ると、2023 年 7 月頃より利用が非推奨(deprecated)となっていました。

This package has been deprecated

Author message:

This package has moved to @smithy/node-http-handler

そして合わせて移行先としてアナウンスされていたのが @smithy/node-http-handler です。

@smithy/node-http-handler で置き換える

@aws-sdk/node-http-handler@smithy/node-http-handler で置き換えてみます。と言っても両者は互換性があるため、パッケージ名を置き換えるだけで移行可能です。

まず @aws-sdk/node-http-handler をアンインストールし、@smithy/node-http-handler をインストールします。

npm uninstall @aws-sdk/node-http-handler
npm install @smithy/node-http-handler

アンインストールおよびインストールできました。

package.json

{
  "dependencies": {
    "@aws-sdk/client-dynamodb": "3.438.0",
+    "@smithy/node-http-handler": "2.1.8"
-    "@aws-sdk/node-http-handler": "3.374.0"
  }
}

そして @aws-sdk/node-http-handler の import を @smithy/node-http-handler に置き換えます。

src/companies.ts

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
// import { NodeHttpHandler } from "@aws-sdk/node-http-handler"; // 削除
import { NodeHttpHandler } from "@smithy/node-http-handler"; // 追加

const dynamodbClient = new DynamoDBClient({
  region: "ap-northeast-1",
  apiVersion: "2012-08-10",
  requestHandler: new NodeHttpHandler({
    connectionTimeout: 200,
    requestTimeout: 1000,
  }),
});

これで型エラーが解消されました。

また最終的に次のようなコードで @smithy/node-http-handler を利用することができました。AWS X-Ray によるキャプチャを AWS SDK クライアントでも利用可能にする captureAWSv3Client や DynamoDB のデータをドキュメント形式で利用可能にする DynamoDBDocument などをあわせて使用した場合でも問題なく動作しました。

src/companies.ts

import {
  ConditionalCheckFailedException,
  DynamoDBClient,
} from "@aws-sdk/client-dynamodb";
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
import { NodeHttpHandler } from "@smithy/node-http-handler";
import { captureAWSv3Client } from "aws-xray-sdk";

const region = process.env.AWS_REGION || "ap-northeast-1";
const apiVersion = "2012-08-10";
const COMPANIES_TABLE_NAME =
  process.env.COMPANIES_TABLE_NAME || "dummy-companiesTableName";

const dynamodbClient = captureAWSv3Client(
  new DynamoDBClient({
    region: region,
    apiVersion: apiVersion,
    requestHandler: new NodeHttpHandler({
      connectionTimeout: 200, // 初回の TCP 接続(SYN SENT)の試行に十分な値を設定。2回目の TCP Retransmission を待たずにタイムアウトさせる
      requestTimeout: 1000, // 1 回あたりの API オペレーションの最大取得サイズ 1 MB の取得に十分な値を設定
    }),
  }),
);
const doc = DynamoDBDocument.from(dynamodbClient);

export interface Company {
  id: string;
  name: string;
}

/**
 * PutItem を使用して会社テーブルにアイテムを put する
 * @param company Company
 */
export const putItem = async (company: Company): Promise<void> => {
  try {
    await doc.put({
      TableName: COMPANIES_TABLE_NAME,
      Item: company,
      ConditionExpression: "attribute_not_exists(id)",
    });
  } catch (e) {
    if (e instanceof ConditionalCheckFailedException) {
      throw new Error("CompanyAlreadyExists");
    }

    throw new Error(JSON.stringify(e));
  }
};

そもそも Smithy って何?

node-http-handler の名前空間の移行先となった Smithy は、AWS が今まで開発してきたインターフェイス定義言語から AWS 固有の実装を分離し、AWS SDK 以外でも広く利用できるようにされたツールです。

Smithy は、Amazon および AWS 内で 10 年以上にわたって広く使用されているインターフェイス定義言語に基づいています。私たちは、他の開発者が独自のサービスに Smithy を使用し、何万ものサービスを構築してきた私たちの長年の経験から恩恵を受けることができるように、Smithy を一般公開することにしました。Smithy の仕様とツールをリリースすることで、開発者がオープンソースの AWS SDK を維持しやすくなるようにしたいと考えています。

リクエストハンドラーなどの AWS SDK のコア仕様ではない実装は切り離して別のエコシステムで育てた方が得策だと判断されたようです。

参考

以上