この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS CDK では TypeScript を始めとした様々な言語でインフラコードを書くことができます。つい先日、Java と .NET にも対応しましたね。
私は、サーバーレスのアプリケーションを実装する際、構築した API に対しても実際にリクエストを送り、レスポンスを確認するようなテストも書きます。結合テスト、E2Eテストなどと呼ばれているテストですね。そのとき、構築したAPIのURLがほしい という状況がよくあります。AWS SAM でサーバーレスアプリケーションを構築していたときは CloudFormation の Output からURLを取得したりもしました。
AWS CDK で構築した場合、aws-ssmが便利です。キーだけ決めておけば、人の手を極力少なくテストコードでURLを呼び出すことができます。早速試しましょう。
環境
パッケージ名 | バージョン |
---|---|
aws-cdk | 1.19.0 |
jest | 24.9.0 |
ts-jest | 24.2.0 |
ts-node | 8.5.4 |
typescript | 3.7.3 |
やること
- AWS CDK で API Gateway (Mock API)をつくる
- URLを Parameter Store に登録する
- テストコードからURLを取得し、リクエストする
AWS CDK で API Gateway(ダミー)をつくる
サンプルとなる API Gateway を構築します。必要な AWS CDK モジュールをインストールしましょう。
> npm install --save-dev @aws-cdk/aws-apigateway @aws-cdk/aws-ssm
次に、CDK のコードを書きます。
lib/eval-cdk19-stack.ts
import * as cdk from '@aws-cdk/core';
import * as apig from '@aws-cdk/aws-apigateway';
import * as ssm from '@aws-cdk/aws-ssm';
// ① function での定義も可能
export function cdkStackEvalCdk(
scope: cdk.Construct,
id: string,
): void {
const stack = new cdk.Stack(scope, id);
// ダミーAPI定義
const api = new apig.RestApi(
stack,
'DummyRestApi',
{
restApiName: `dummy`,
endpointTypes: [apig.EndpointType.REGIONAL],
deployOptions: {
stageName: 'v1'
},
},
);
// GET /user
const userResource = api.root.addResource('user');
resourceIntegrationGetUser(userResource, 'GET');
// ② URLを Parameter Store に登録
new ssm.StringParameter(stack, 'UserApiUrl', {
parameterName: 'UserApiUrl',
stringValue: api.url,
});
}
function resourceIntegrationGetUser(
self: apig.Resource,
method: string,
): void {
const methodOptions: apig.MethodOptions = {
methodResponses: [
{
statusCode: '200',
},
],
};
const dummyUser = {
name: 'wada',
description: 'sorry for late'
};
const integration = new apig.MockIntegration({
requestTemplates: {
'application/json': `{
"statusCode": 200
}`,
},
integrationResponses: [
{
statusCode: '200',
responseTemplates: {
'application/json': JSON.stringify(dummyUser),
},
},
],
});
self.addMethod(method, integration, methodOptions);
}
ポイントを見ていきます。
1.AWS CDK の Construct 定義は function でもできる
cdk init
で出来上がる最初のコードが cdk.Construct
をオーバーライドする形になっているので勘違いしがちですが、function でも書けます。そのときは、function内で const stack = new cdk.Stack(scope, id);
のようにして Stacks を、 const stackConstruct = new cdk.Construct(scope, id);
のようにしてConstructを定義できます。
2. aws-ssm
モジュールを使って Parameter Store に登録する
Parameter Store に登録しようとなったとき、私が最初に思いついたのは AWS SDK(CDKではなく)を使って API Gateway のURLを登録することでした。次のようなイメージです:
const params = {
Name: 'UserApiUrl',
Type: 'String',
Value: apig.url
};
ssm.putParameter(params).promsise();
しかしこれではうまくいきません。この処理が呼ばれる(= Parameter Store に登録される)時点では、API Gateway はデプロイが完了しておらず、URLが確定していないためです。それじゃあ一体どんな値が入っているのかというと、仮の値が入っています。 console.log
してみると https://${Token[TOKEN.12]}.execute-api.${Token[AWS::Region.4]}.${Token[AWS::URLSuffix.1]}/${Token[TOKEN.18]}/
という値が出力されました。落ちつて考えれば当たり前で、依存関係を解決してデプロイが完了するまでは、API Gateway のURLも確定せず、 Parameter Store に登録することもできませんね。
ではどうするかというと、API Gateway の URLも Parameter Store の "AWSリソース" として構築してしまいます。それをやってくれるのが aws-ssm
です。
new ssm.StringParameter(stack, 'UserApiUrlParameter', {
parameterName: 'UserApiUrl',
stringValue: api.url,
});
このように StringParameter
を登録することで確定した URL をParameter Storeに追加できます。デプロイしましょう。
> cdk deploy
Parameter Store に登録されていることを確認してください。
aws ssm get-parameter --name UserApiUrl
{
"Parameter": {
"Name": "UserApiUrl",
"Type": "String",
"Value": "https://xxxxyyyzzz.execute-api.ap-northeast-1.amazonaws.com/v1/",
"Version": 1,
"LastModifiedDate": 1576684620.448,
"ARN": "arn:aws:ssm:ap-northeast-1:xxxxxxxxxxx:parameter/UserApiUrl"
}
}
これで準備完了です。
テストコードからURLを取得し、リクエストする
テストコードのほうは AWS SDK を使います。インストールしておきましょう。
> npm install --save-dev aws-sdk axios
ダミーAPIに対してリクエストし、レスポンスを確認するようなテストを書きます。
test/eval-cdk19-e2e.test.ts
import * as aws from 'aws-sdk';
import axios from 'axios';
const ssm = new aws.SSM({
region: 'ap-northeast-1'
});
test('Dummy API', async () => {
// ① Parameter Store からURLを取得
const param = await ssm.getParameter({
Name: 'UserApiUrl'
}).promise();
const url = param.Parameter?.Value!;
const userEndpoint = `${url}/user`;
// ② URLに対してリクエスト、レスポンスボディを確認する
const response = await axios.get(userEndpoint);
expect(response.data).toEqual({
name: 'wada',
description: 'sorry for late'
});
});
デプロイされたダミーAPIのURLを Parameter Store から取得し、リクエストを送ることができました。ここまでURLのコピペは行っていませんし、特別なスクリプトも書いていません。いい感じですね。
まとめ
aws-ssm
を使うことで、 AWS CDK デプロイ時に API Gateway の URL も登録できました。その後、テストコードで実際に Parameter Store から取得してテストを行いました。特にサーバーレスアプリケーションなど、多くのサービスが連携するタイプのシステムでは、実際にデプロイしての動作確認が有効です。そのときに、APIを始めとしたアクセス可能なエンドポイントを Parameter Store に登録しておけば捗ると思います。参考になれば幸いです。