概要
AWS Amplify では11月のアップデートで追加された「オーバーライド」機能により、バックエンドリソースを AWS CDK でカスタマイズできるようになりました
2021/11/23 リリースの v7.5.0 から API カテゴリーでも利用可能となっていて、この新機能を利用して今までできなかった OpenAPI 定義ファイルを利用した API Gateway (REST) リソースのデプロイをしてみたいと思います。
注意:
執筆時点(2021/12/13 時点)ではこのオーバーライド機能でできることが一部制限されており、ワークアラウンドで対応した部分を含んでいます(詳細は後述します)。今後修正加筆される可能性があることをご了承ください。
やってみる
構成
AWS Amplify で以下のようなシンプルな OpenAPI 定義 (OAS3) をもとに API Gateway + Lambda で構成された REST API のバックエンドを作成してみます。
環境
執筆時は AWS Cloud9 上で Amplify CLI v7.6.3 をインストールして実施しました。
手順
1. Amplify CLI のセットアップとプロジェクトの初期化
Amplify CLI をまだインストールしていない場合は、公式ドキュメントの記載を参考にインストールし、その後 Amplify CLI のセットアップを行います。
npm install -g @aws-amplify/cli
次に、公式ドキュメントの「Initialize new project」の記載にしたがってプロジェクトを初期化しましょう(詳細な手順は割愛します)。
amplify init
今回は ap-northeast-1
リージョンに OpenAPISample
というプロジェクト名で作成してみました。
2. Amplify CLI での API カテゴリの追加とデプロイ
Amplify プロジェクトを初期化したら、以下のコマンドから Lambda 関数とともに REST API のバックエンドを追加していきます。
amplify add api
今回は以下のように回答を選択しました。
- Select from one of the below mentioned services:
REST
- Provide a friendly name for your resource to be used as a label for this category in the project:
Greeting
- Provide a path (e.g., /book/{isbn}):
/hello
- Only one option for [Choose a Lambda source]. Selecting [
Create a new Lambda function
]. - Provide an AWS Lambda function name:
Hello
- Choose the runtime that you want to use:
NodeJS
- Choose the function template that you want to use:
Hello World
- Do you want to configure advanced settings?
No
- Do you want to edit the local lambda function now?
No
- Restrict API access? (Y/n)
no
- Do you want to add another path? (y/N)
no
3. REST API のデプロイ(初回)
初期設定のまま API をデプロイしてみます。
amplify push -y
しばらく待って All resources are updated in the cloud
と表示されたら、マネジメントコンソールから「Greeting」という名前の API Gateway リソースができていることを確認します。
また、「Hello-dev」という名前の Lambda 関数も作成されていることを確認します。
ここまでで、テンプレートをもとに /hello
というパスへのリクエストに対して Lambda から "Hello from Lambda!"
というレスポンスを返す API がデプロイされました。
curl コマンドで以下のようなレスポンスとなることを確認しましょう。
$ curl https://**********.execute-api.ap-northeast-1.amazonaws.com/dev/hello
"Hello from Lambda!"
(補足): **********
の部分にはデプロイされた API Gateway のリソース ID を入力します
4. OpenAPI 定義ファイルの作成と配置
手順 3. でデプロイした API を OpenAPI 定義ファイルの設定をもとに更新していきます。
以下のような OpenAPI 定義ファイルを openapi.json
という名前で作成し、プロジェクトディレクトリ配下の amplify/backend/api/Greeting
に配置します。
{
"openapi" : "3.0.1",
"info" : {
"title" : "Greeting",
"version" : "1.0.0"
},
"paths" : {
"/hello" : {
"get" : {
"parameters" : [ {
"name" : "name",
"in" : "query",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"responses" : {
"200" : {
"description" : "200 response",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Response"
}
}
}
}
},
"x-amazon-apigateway-request-validator" : "クエリ文字列パラメータおよびヘッダーの検証",
"x-amazon-apigateway-integration" : {
"httpMethod" : "POST",
"uri" : "<PLACEHOLDER>",
"responses" : {
"default" : {
"statusCode" : "200"
}
},
"passthroughBehavior" : "when_no_match",
"contentHandling" : "CONVERT_TO_TEXT",
"type" : "aws_proxy"
}
}
}
},
"components" : {
"schemas" : {
"Response" : {
"required" : [ "message" ],
"type" : "object",
"properties" : {
"message" : {
"type" : "string"
}
}
}
}
},
"x-amazon-apigateway-request-validators" : {
"クエリ文字列パラメータおよびヘッダーの検証" : {
"validateRequestParameters" : true,
"validateRequestBody" : false
}
}
}
解説
ここでは「Greeting」API を編集し、/hello
パスの GET
メソッドに対するリクエストを、手順 3. で作成した「Hello」という Lambda 関数に渡すように定義しています。
x-amazon-
から始まるフィールドは Amazon 独自の設定項目です。
このうち x-amazon-apigateway-integration
というフィールドでは Lambda 関数のプロキシ統合の設定をしています。<PLACEHOLDER>
という箇所は、後で Amplify でデプロイされた Lambda 関数の情報で置換します。
また x-amazon-apigateway-request-validator
というフィールドで、API Gateway の機能を使って name
というクエリパラメータが存在するかの検証をする設定をしています。
5. オーバーライド機能の有効化と OpenAPI 定義ファイルの適用
手順 3. でデプロイした API に対して、オーバーライド機能を使って OpenAPI 定義ファイルをインポートしていきます。
まず、以下のコマンドからオーバーライド機能を有効化します。
amplify override api
回答には以下のように選択します。
- Do you want to edit override.ts file now? (Y/n)
yes
- Choose your default editor:
None
=> 好きなものを選択
すると、以下のようにプロジェクトディレクトリ配下の amplify/backend/api/Greeting
に override.ts
というファイルが作成されます。
このファイルを編集することで API Gateway リソースの定義の編集ができるようになります。(詳細は以下のリンク先を参照)
override.ts
を以下のように編集します。
// This file is used to override the REST API resources configuration
import { AmplifyApiRestResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyApiRestResourceStackTemplate) {
// (1) OpenAPI 定義をファイルから読込
const openApiJson = require('../openapi.json');
// (2) amplify-meta.json から Lambda 関数の情報を取得
const amplifyMetaJson = require('../../../amplify-meta.json');
const helloFunctionRegion = amplifyMetaJson.function.Hello.output.Region;
const helloFunctionArn = amplifyMetaJson.function.Hello.output.Arn;
// (3) OpenAPI 定義の環境に応じて変わる部分を編集
openApiJson.paths['/hello']['get']['x-amazon-apigateway-integration']['uri'] = `arn:aws:apigateway:${helloFunctionRegion}:lambda:path/2015-03-31/functions/${helloFunctionArn}/invocations`
// (4) 編集した OpenAPI 定義を resources.restApi の body プロパティに代入
resources.restApi.body = openApiJson;
// (5) API のデプロイメント更新のための CloudFormation リソースの変更
resources.deploymentResource.overrideLogicalId('DeploymentAPIGW' + openApiJson.info.title + Date.now());
}
解説
(1) 配置した OpenAPI 定義をファイルから読込
OpenAPI 定義ファイルを JSON 形式で作成し require
コマンドで読み込んでいます(注意点参照)。override.ts
はビルド時にトランスパイルされ、override.js
という名前でプロジェクトディレクトリ配下の amplify/backend/api/Greeting/build
に配置されますが、このロケーションからの相対パスを指定するようにしています。
注意点:
現状 override.ts 内に記載した処理は vm2 モジュールで提供される sandbox 内で実行されますが、この sandbox では現状 fs, util, os といった Node.js の built-in モジュールが利用できません。これは以下の Issue にも記載があります。
OpenAPI 定義をファイルから読み込む場合、本来は swagger-parser などの YAML ファイルにも対応した OpenAPI 定義のパースに特化したツールを使うのがよいのですが、このツールでは util などの前述の built-in モジュールを利用する必要があるため、現状では利用できずこのようなワークアラウンドの方法をとっています。
参考:swagger-parser
(2) amplify-meta.json から Lambda 関数の情報を取得
デプロイした「Hello」という名前の Lambda 関数の情報を amplify/backend/amplify-meta.json
から読み込んでいます。
注意点:
Amplify にはプロジェクトのバックエンドリソースを CDK 向けにエクスポートする機能がある(詳細は以下のリンク先を参照)ため、こちらを活用するのがよいと思われますが、前の話と同様、現状 fs, util, os といった Node.js の built-in モジュールが利用できないためその方法がとれず、ワークアラウンドでこのように対応しています。
ちなみに amplify-meta.json の中身は AWS 上にバックエンドがデプロイされるまで ARN などの値は参照できないため、例えば Lambda が未デプロイの場合は amplify push function を実行して先にデプロイしておく必要があることに注意しましょう。
(3) OpenAPI 定義の環境に応じて変わる部分を編集
OpenAPI 定義の x-amazon-apigateway-integration
というフィールドでは Lambda 関数の URI を指定しています(詳細は以下のリンク先を参照)。
ここは Amplify の環境に応じて動的に変わる部分のため、OpenAPI 定義をファイルから読み込んだ後の JSON オブジェクトに対して上書きしています。他にも環境に応じて変わるような項目があれば、同じように上書きしておきます。
(4) 編集した OpenAPI 定義を resources.restApi の body プロパティに代入
override.ts
の override 関数の引数にあたる resources
の中身を編集します。resources.restApi
には 以下リンク先の AWS::ApiGateway::RestApi
の設定が入っており、この body
プロパティに代入することで OpenAPI 定義を API に適用できます。
(5) API のデプロイメント更新のための CloudFormation リソースの変更
APIのリソースの定義の修正は Deployment を更新することで反映されますが、CloudFormation の制約により AWS::ApiGateway::RestApi
の設定を変更しただけでは Deployment は更新されません。
以下の Issue の記載にしたがい、API Gateway の Deployment の CloudFormation リソース名 (Logical ID) を変更しておきます。すると AWS::ApiGateway::RestApi
と同時にデプロイされるようになります。
今回は CloudFormation リソース名 (Logical ID) の末尾にタイムスタンプをつける対応をし、これにより amplify push
をする度に毎回 Deployment がデプロイされるようにしていますが、これを OpenAPI 定義の中のバージョンなどと紐づけることで、更新タイミングをバージョン更新時のタイミングに制御することもできます。
6. Lambda 関数の編集
OpenAPI の定義に合わせてプロジェクトディレクトリ配下の amplify/backend/function/Hello/src
のパスにある Lambda 関数を以下のように編集します。
exports.handler = async (event) => {
const name = event.queryStringParameters.name;
const response = {
statusCode: 200,
body: JSON.stringify({message: `Hello, ${name}!`}),
};
return response;
};
クエリパラメータの name
の値が出力メッセージに反映されるように変更しています。
7. REST API のデプロイ(オーバーライド有効化後)
上記で変更した API Gateway および Lambda 関数をデプロイします。
amplify push -y
しばらく待って All resources are updated in the cloud
と表示されたら、マネジメントコンソールから「Greeting」という名前の API に変更が反映されていることを確認します。
また、「Hello-dev」という名前の Lambda 関数にも変更が反映されていることも確認します。
curl コマンドで以下のように name
というクエリパラメータを付与した場合に、以下のように出力メッセージにそれが反映されることを確認しましょう。
$ curl https://**********.execute-api.ap-northeast-1.amazonaws.com/dev/hello?name=hogehoge
{"message":"Hello, hogehoge!"}
(補足): **********
の部分にはデプロイされた API Gateway のリソース ID を入力します
一方で、クエリパラメータに name
パラメータを付与せずに実行した場合の結果が以下のようになることを確認します。
$ curl https://xjuilfsqmb.execute-api.ap-northeast-1.amazonaws.com/dev/hello
{"message": "Missing required request parameters: [name]"}
OpenAPI 定義の中で API Gateway の機能によりクエリパラメータの検証を行う設定が正しく反映されています。
まとめ
AWS Amplify のオーバーライド機能を使うことで、これまでできなかった OpenAPI 定義ファイルをベースにした REST API のデプロイができることが確認できました
API 仕様書をベースにするスキーマファーストな開発を採用するケースが増えている昨今ですので、ぜひ Amplify による REST API 開発でもこの手法を活用していきましょう。
今回いくつかワークアラウンドで対応した部分もありますが、今後 Issue への対応が行われ、さらに使い勝手がよくなっていくことを期待しましょう。
それではまた!