🍬

AWS SDK for JavaScript v3 にアップデートした

4 min read

はじめに

TypeScript で使っている aws-sdk のバージョンを上げようと思って調べたところ、リポジトリの README に v3 のアナウンスが出ていることに気づきました。

Version 3.x Now Available
The version 3.x of the AWS SDK for JavaScript is generally available. For more information see the Developer Guide or API Reference.

https://github.com/aws/aws-sdk-js

https://github.com/aws/aws-sdk-js-v3

実は2020年12月から公開されていたようです。

https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-sdk-javascript-version-3-generally-available/

この機会に v3 にアップデートをしたのですが、公開から半年以上経っていますが日本語の情報が少なかったり、最近の情報でも v2 の書き方が紹介されていたりするため、作業内容をまとめました。

サンプルコード

今回実際に書き換えた AWS SES の SendEmail のコードを例にあげます。

v2

v2 ではこのような書き方で、この書き方をしている情報が多いかと思います。

import * as AWS from "aws-sdk";

const ses = new AWS.SES({ region: "us-east-1" });

async function handler() {
  const params: AWS.SES.SendEmailRequest = {
    Source: "from@example.com",
    Destination: { ToAddresses: ["to@example.com"] },
    Message: {
      Subject: { Data: "test subject" },
      Body: {
        Text: { Data: "test body" },
      },
    },
  };
  
  await ses.sendEmail(params).promise()
}

v3 (Bare-bones clients)

v3 の基本の書き方はこのように Command インスタンスを作りクライアントの .send() に渡すようになりました。

import {
  SESClient,
  SendEmailCommand,
  SendEmailCommandInput,
} from "@aws-sdk/client-ses";

const ses = new SESClient({ region: "us-east-1" });

async function handler() {
  const params: SendEmailCommandInput = {
    Source: "from@example.com",
    Destination: { ToAddresses: ["to@example.com"] },
    Message: {
      Subject: { Data: "test subject" },
      Body: {
        Text: { Data: "test body" },
      },
    },
  };
  
  const command = new SendEmailCommand(params);
  
  await ses.send(command);
}

v3 (Aggregated clients)

実は SESClient のような XXXClient から Client を取ったサービス名だけのクラスを使えば Command を作らず v2 とほぼ同じような書き方をすることができます。

import { SES, SendEmailCommandInput } from "@aws-sdk/client-ses";

const ses = new SES({ region: "us-east-1" });

async function handler() {
  const params: SendEmailCommandInput = {
    Source: "from@example.com",
    Destination: { ToAddresses: ["to@example.com"] },
    Message: {
      Subject: { Data: "test subject" },
      Body: {
        Text: { Data: "test body" },
      },
    },
  };
  
  await ses.sendEmail(params);
}

この場合はバンドルサイズが比較的大きくなる可能性があるので注意するよう README に書かれています。

If you want to use non-modular (v2-like) interfaces, you can import client with only the service name (e.g DynamoDB), and call the operation name directly from the client:

If you use tree shaking to reduce bundle size, using non-modular interface will increase the bundle size as compared to using modular interface.

https://github.com/aws/aws-sdk-js-v3#getting-started

変更点

個別に @aws-sdk/client-* パッケージをインポートするようになった

大きな変更点として、今までは aws-sdk をまるごとインポートしていましたが、 v3 では必要なパッケージを個別に依存関係に追加してインポートするようになりました。

これにより必要なモジュールのみをバンドルすることができバンドルサイズが小さくできることが見込めます。

注意点として、 AWS Lambda の Node.js ランタイムではデフォルトで aws-sdk がインストールされていますが、 v3 のパッケージは現時点ではデフォルトでは使えないため、 ソースコードと一緒にアップロードするか Lambda Layer を使って追加してあげる必要があり、かえってバンドルサイズが大きくなってしまいます。

AWS.config によるグローバルな設定ができなくなった

import * as AWS from "aws-sdk";

という書き方をしなくなり個別にパッケージをインポートするようになったので v2 まで使えた AWS.config のような書き方ができなくなりました。

README によるとこのようなグローバルな設定は挙動が分かりづらいなどの理由により意図的にオミットされたようです。

Middleware Stack

クライアントの middlewareStack を使うことで、 API コール時に毎回呼び出されるミドルウェアを追加することができます。

(README のサンプルコードを引用)

const client = new DynamoDB({ region: "us-west-2" });
client.middlewareStack.add(
  (next, context) => (args) => {
    args.request.headers["Custom-Header"] = "value";
    console.log("\n -- printed from inside middleware -- \n");
    return next(args);
  },
  {
    step: "build",
  }
);
await client.listTables({});

このサンプルコードのように全ての API コールに対してヘッダーを編集したりロギングしたりするときに便利そうです。

.promise() が不要になった

個人的にはこれが一番大きいです。
「あれ、なんで動かないのかな」と思ったら .promise() を忘れていて時間を無駄にしたことが何度もあるので...

初めて触る人にとってもハマりどころの少ないインタフェースになったように思います。

まとめ

あまり情報がなかったり、公式のドキュメントも v2 の書き方になっていたりしますが、同じ AWS の API をラップしたものなため、基本的にインプットとアウトプットは変わっていないはずです。

そこまで大きな労力はなく書き換え可能かと思われますので、アップデートを検討してみてはいかがでしょうか。