AWS CDK で API Gateway を作った後、テストコードに URL を伝えるのは Parameter Store が便利だよという話
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 モジュールをインストールしましょう。
1 | > npm install --save-dev @aws-cdk /aws-apigateway @aws-cdk /aws-ssm |
次に、CDK のコードを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 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を登録することでした。次のようなイメージです:
1 2 3 4 5 6 | 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
です。
1 2 3 4 | new ssm . StringParameter ( stack , 'UserApiUrlParameter' , { parameterName : 'UserApiUrl' , stringValue : api . url , } ) ; |
このように StringParameter
を登録することで確定した URL をParameter Storeに追加できます。デプロイしましょう。
1 | > cdk deploy |
Parameter Store に登録されていることを確認してください。
1 2 3 4 5 6 7 8 9 10 11 | aws ssm get-parameter --name UserApiUrl { "Parameter" : { "Name" : "UserApiUrl" , "Type" : "String" , "Version" : 1, "LastModifiedDate" : 1576684620.448, "ARN" : "arn:aws:ssm:ap-northeast-1:xxxxxxxxxxx:parameter/UserApiUrl" } } |
これで準備完了です。
テストコードからURLを取得し、リクエストする
テストコードのほうは AWS SDK を使います。インストールしておきましょう。
1 | > npm install --save-dev aws-sdk axios |
ダミーAPIに対してリクエストし、レスポンスを確認するようなテストを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 に登録しておけば捗ると思います。参考になれば幸いです。