AWS Amplify Gen 2 と AWS CDK で爆速アプリ開発 & 拡張 !
~ 第 1 回 AWS Amplify Gen 2 から始める AWS CDK 入門
Author : 菊地 晏南
こんにちは ! ソリューションアーキテクトの菊地です。
みなさん、AWS Amplify というサービスをご存知でしょうか?AWS Amplify は、Next.js や Vue.js などのフロントエンドフレームワークを簡単にホスティングし、フロントエンドから呼び出すためのバックエンドも構築してフルスタックのアプリケーションを楽に作ることができる便利なサービスです。
そんな AWS Amplify は今年 5 月に、コードファーストな開発体験を提供する AWS Amplify Gen 2 の一般提供が開始され、バックエンドの構築に AWS Cloud Development Kit (AWS CDK) を利用するようになったことでより柔軟にバックエンドを構築できるようになりました。しかし、AWS CDK に馴染みのない方の中には、「CDK ?何それ美味しいの?」状態になって手を出せずにいる方もいるのではないでしょうか。
本記事では、AWS Amplify のバックエンドをカスタマイズする際に必要となる AWS CDK の基礎知識をご紹介します。みなさんが Amplify x CDK マスターになる第一歩を手助けする、そんな記事です ! また、概要でも触れている通り本記事は連載のうちの初回記事ですので、 そもそも AWS Amplify って何?という方は続編の AWS Amplify 入門記事にご期待ください !
・・・おや ? ちょうど AWS Amplify と AWS CDK について話している声が聞こえますね...
はじめに
Amplify 好きの稲田 :
あのー、AWS Amplify のチュートリアルを見ているのですが、CDKの部分がよくわからなくて。あまり馴染みがないんですよね〜。
CDK 好きの菊地 :
なるほど、CDK のことならお任せを!どれどれ・・・。CDK でカスタマイズしてる部分については説明できそうです ! どのあたりが分からないですか ?
稲田 :
Stack とか Construct とかの概念がよく分からなくて・・・。Amplify Gen 1 では Amplify CLI を使ってサクッと作っていたので、AWS Amplify Gen 2 になっていざ CDK でカスタマイズしようと思ったのですが、何が何だかさっぱりです。
菊地 :
見る限り CDK の基本的な書き方がわかれば簡単なカスタマイズはできそうなので、一つ一つ追っていきましょう!普段 CDK では見たことがない部分も少しあるので、逆にこの辺りを教えて欲しいのですが・・・。
稲田 :
もちろんです ! ぜひ菊地さんも Amplify 使ってみてください !
本記事は、「AWS Amplify と AWS CDK で爆速アプリ開発 & 拡張 !」シリーズの第一弾です。今後は AWS Amplify 利用時に遭遇しうる AWS CDK 周りのトラブルの解決方法や、AWS Amplify に馴染みのない向けの AWS Amplify 入門記事などを連載予定です。乞うご期待 !
Amplify 好きの稲田 :
今やっているのが、 Amplify のドキュメントにある「Set up Amplify REST API」です。Amplify で作ったアプリケーションに Amazon API Gateway と AWS Lambda を使った REST API を追加しようとしているようなのですが、 いまいちよくわからず。
CDK 好きの菊地 :
そしたら上から順番に見ていきましょうか。
本記事では、稲田さんが実施している「Set up Amplify REST API」のサンプルコードをもとに、AWS CDK の基礎について紹介していきます。皆さんも一緒にコードを読み解いて、AWS CDK の概念や書き方について学んでいきましょう。
AWS Amplify バックエンドの定義
早速、Amplify のアプリケーションが CDK でどのようにカスタマイズされているか見ていきましょう。
import { defineBackend } from "@aws-amplify/backend";
import { Stack } from "aws-cdk-lib";
import {
AuthorizationType,
CognitoUserPoolsAuthorizer,
Cors,
LambdaIntegration,
RestApi,
} from "aws-cdk-lib/aws-apigateway";
import { Policy, PolicyStatement } from "aws-cdk-lib/aws-iam";
import { myApiFunction } from "./functions/api-function/resource";
import { auth } from "./auth/resource";
import { data } from "./data/resource";
const backend = defineBackend({
auth,
data,
myApiFunction,
});
まず、AWS Amplify のライブラリで提供されている defineBackend 関数を使って Amplify アプリケーションのバックエンドリソースを定義しています。ここでは、 auth で Amazon Cognito を用いた認証を、data で AWS AppSync を用いた API エンドポイントや Amazon DynamoDB を定義しています。myApiFunction は同じチュートリアルの別の箇所で定義された AWS Lambda 関数に対応しています。Lambda 関数の実装が気になる方はぜひ ドキュメント本文 を読んでみてください。
ここまでの構成はこのアーキテクチャ図のようになっています。
AWS CDK によるカスタマイズ
さて、ここからが AWS CDK を使ったカスタマイズの本題です。例として、バックエンドに Amazon API Gateway の REST API を作成し、AWS Lambda を呼び出したいとしましょう。その場合、このような書き方が考えられます。
// create a new API stack
const apiStack = backend.createStack("api-stack");
// create a new REST API
const myRestApi = new RestApi(apiStack, "RestApi", {
restApiName: "myRestApi",
deploy: true,
deployOptions: {
stageName: "dev",
},
defaultCorsPreflightOptions: {
allowOrigins: Cors.ALL_ORIGINS, // Restrict this to domains you trust
allowMethods: Cors.ALL_METHODS, // Specify only the methods you need to allow
allowHeaders: Cors.DEFAULT_HEADERS, // Specify only the headers you need to allow
},
});
ここでは、api-stack という「Stack」を作成し、その Stack 内に API Gateway の「Construct」を使って myRestApi という REST API を作成しています。サンプルコードを詳しく見ていく前に、 Stack、Construct などの CDK の基本的な概念について理解しておきましょう。
AWS CDK の基礎:App、Stack、Construct
Stack とは、AWS CDK におけるデプロイ可能な最小単位で、AWS CloudFormation の Stack に対応しています。 AWS CloudFormation は AWS CDK と同じく Infrastructure as Code (IaC) ツールの一つで、JSON か YAML の形式でインフラストラクチャを定義することができます。実は CDK の裏側では CloudFormation が使われており、CloudFormation がリソースを管理する際の単位である Stack と、CDK における Stack が一対一で対応しています。
また、 Construct とは AWS CDK における最も基本的な要素の一つで、Construct の中に 1 つ以上のインフラストラクチャリソースを表現できます。今回のサンプルでは、 Amazon API Gateway の REST API Construct を使って API Gateway のリソースを定義しています。
さらに、このサンプルコードでは明記されていませんが、AWS CDK には App という概念があります。App の中に CDK の Stack を定義でき、複数の AWS アカウント、リージョンの Stack を一つの App にまとめることができます。
AWS Black Belt オンラインセミナー AWS CDK の基本的なコンポーネントと機能 (Basic #2)
(画像をクリックすると拡大します)
Construct の初期化引数:scope、id、props
これらの App、Stack、Construct が AWS CDK における基本的な概念です。これらを意識して、改めてサンプルコードを見てみましょう。
// create a new API stack
const apiStack = backend.createStack("api-stack");
ここでは Amplify ライブラリで提供されている createStack 関数を使って CDK の Stack が定義されています。通常は App を定義してからその中に Stack を定義しますが、createStack 関数を使用する場合は Amplify ライブラリが内部で CDK App を定義してくれているため、ここでは明示的に App を定義する必要はありません。ここから Stack の中に AWS リソースを定義していきます。
// create a new REST API
const myRestApi = new RestApi(apiStack, "RestApi", {
restApiName: "myRestApi",
deploy: true,
deployOptions: {
stageName: "dev",
},
defaultCorsPreflightOptions: {
allowOrigins: Cors.ALL_ORIGINS, // Restrict this to domains you trust
allowMethods: Cors.ALL_METHODS, // Specify only the methods you need to allow
allowHeaders: Cors.DEFAULT_HEADERS, // Specify only the headers you need to allow
},
});
ここでは API Gateway の REST API が定義されています。RestApi の初期化の引数に注目してみましょう。AWS CDK の Construct の初期化では scope 、id、props の 3 つの引数を指定します。
new Construct([scope], [id], [props])
scope
まず、第一引数には先ほど定義した apiStack が渡されています。こうすることで、 apiStack の直下に API Gateway のリソースを定義することができます。この第一引数はスコープと呼ばれ、 Construct ツリー の親を指定します。 Construct ツリーは Construct の階層構造のことで、App の下に Stack が、Stack の下に Construct が存在します。今回は scope として Stack を渡すことで Stack の下に Construct があるという親子関係ですが、 scope として Construct を指定することで Construct の下に Construct を定義し、複数のリソースを束ねることもできます。
ちなみに、 Stack も Construct の一つなので、通常は Stack 定義時に scope として App を指定することで、 App の中に Stack を定義します。ただ、今回のサンプルコードでは Stack 定義時に "api-stack" という Stack 名しか定義しておらず、 App を指定していません。
id
次に、第二引数には "RestApi" という文字列が渡されています。これは Construct ID と呼ばれ、スコープ内での一意の識別子を指定します。スコープ内で一意であれば、 Construct ID は自由に決めることができます。 Construct ID は実際にデプロイされるリソースのリソース名や、AWS CloudFormation における論理 ID の一部として利用されます。
props
最後に第三引数にはさまざまなキーと値が渡されています。これはプロパティと呼ばれ、リソースの初期設定値を指定します。例えば、今回のサンプルコードでは restApiName: "myRestApi" でリソース名を指定しています。どのキーにどのような値を指定できるか調べるには AWS CDK の API リファレンス を参照します。props で指定する設定値の中には必須のものとオプションのものがあり、CDK では AWS のベストプラクティスに沿ったデフォルト値が設定されているため、全ての値がオプションである場合もあります。
また、今回は restApiName: "myRestApi" で REST API のリソース名を指定していますが、同じ Stack 定義を使って複数の Stack を作成する場合に重複する可能性があるため、必要がなければ指定しないようにするか、例えば env という環境ごとの変数を定義して restApiName: `myRestApi-${env}` のように重複しないようにするのがいいでしょう。
これら、scope 、id、props の 3 つの引数を指定することで、API Gateway のリソースが作成されていることがご理解いただけたと思います。カスタマイズも含めたアーキテクチャ図はこのようになっています。
Amazon API Gateway の認証に Amazon Cognito を利用する
最後に、作成した Amazon API Gateway の REST API に Amazon Cognito を利用した認証を実装してみましょう。すでに auth で定義されている Amazon Cognito のユーザープールを参照して利用します。まず、Amazon Cognito のオーソライザーを定義します。
// create a new Cognito User Pools authorizer
const cognitoAuth = new CognitoUserPoolsAuthorizer(apiStack, "CognitoAuth", {
cognitoUserPools: [backend.auth.resources.userPool],
});
ここでは、auth で定義されている Amazon Cognito のユーザープールを backend.auth.resources.userPool で参照しています。同様に、data.resources のように Amplify ライブラリの裏側で定義された AWS リソースを参照することができます。次に、定義したオーソライザーを REST API のメソッドに紐づけます。
// create a new resource path with Cognito authorization
const booksPath = myRestApi.root.addResource("cognito-auth-path");
booksPath.addMethod("GET", lambdaIntegration, {
authorizationType: AuthorizationType.COGNITO,
authorizer: cognitoAuth,
});
authorizationType で Amazon Cognito で認証を行うように設定し、authorizer で Amazon Cognito のオーソライザーを指定します。ちなみに、lambdaIntegration は AWS Amplify のドキュメント本文 の別の箇所で定義されている API Gateway の Lambda プロキシ統合 です。これで 、API Gateway の認証に Amazon Cognito を利用する設定が完了です !
ここまで来れば、CDK を使ったカスタマイズをするための基礎についてはバッチリです。
CDK 好きの菊地 :
・・・ということなんです。
Amplify 好きの稲田 :
なるほど ! あまりよくわからず敬遠してしまっていましたが、紐解いてみると Stack の中で Construct を使ってリソースを定義していて、リソースの定義も CDK の API リファレンスに載っているようなプロパティを指定しているだけなんですね。今見ればスッキリ書けて読みやすいです !
菊地 :
そういうことなんです ! 理解してもらえてよかったー。
おわりに
いかがだったでしょうか ?
本記事では AWS Amplify Gen 2 でバックエンドを拡張する際に必要となる AWS CDK の基礎についてご紹介しました。 Stack や Construct などの基本的な概念さえ押さえてしまえば、柔軟に AWS Amplify のバックエンドリソースを構築できることがおわかりいただければ幸いです !
筆者プロフィール
菊地 晏南 (きくち あなん)
アマゾン ウェブ サービス ジャパン合同会社
技術統括本部 ソリューションアーキテクト
ソリューションアーキテクトとして、クラウドを活用されているお客様の技術支援を行っています。
バスケと旅行とカフェ巡りが趣味です。
AWS を無料でお試しいただけます