AWS CDKでurl短縮サービスをサーバーレスで作ってみた

長い長いリンクを共有する際に短縮したいねということがあったので、いろいろ調べた結果、いくつかのチュートリアルが見つかりました。
[AWS Cloud Development Kit(CDK)でURL短縮サービスを作ってみた](https://dev.classmethod.jp/articles/implement-url-shorten-service-with-aws-cdk-python/)
[自社で使えるURL短縮サービスを低コストにサーバーレスで構築した話](https://engineer.retty.me/entry/2019/05/31/120000)
一旦後者のチュートリアルで作成したものの、CloudFrontのカスタムドメイン設定がどうにもうまくいかなかったのとコンソールをポチポチするのに嫌気がさしたので、前者のチュートリアルをベースにカスタマイズするという結果になりました。

# 作成の流れ
* aws cdkのセットアップ
* CloudFormationのstackの作成
* カスタマイズ

# cdkセットアップとチュートリアル
[AWS CDKとは](https://aws.amazon.com/jp/cdk/)
簡単に言うとAWS上で必要なリソースを、書き慣れた言語で記述されたファイルを元に作成できるツールです。CloudFormationを直書きするときはJsonかYamlで設定ファイルを作成しますが、それをPythonとかTypescriptとかで代替できる様なものです。IAMを一部適切に設定してあげる必要がありますが、あーしてこーしてと言うのを感覚で書いていくことができるので個人的には好きです。があまりサンプルが多くないのと、AWSの用語やリソースの関連の仕組みを熟知していないとスラスラかけないのがボトルネックではあります。（個人的に、LambdaとかApiGatewayとか触り慣れてなかったので苦労しました）
[CDKのAPI一覧](https://docs.aws.amazon.com/cdk/api/latest/python/index.html)

cdkの導入とベースになるstackの作成はまんまチュートリアルと同じなので、割愛します。下記を参照してください。
[AWS Cloud Development Kit(CDK)でURL短縮サービスを作ってみた](https://dev.classmethod.jp/articles/implement-url-shorten-service-with-aws-cdk-python/)

#APIGatewayに鍵をつける
```python:url_shortener_stack.py
api = aws_apigateway.LambdaRestApi(self, "UrlShortenerApi", handler=handler)
```
`LambdaRestApi`を使った場合のAPIGatewayへの鍵の付け方が見つからず、代わりに`RestApi`を使用して実装した。

```python:url_shortener_stack.py
api = aws_apigateway.RestApi(self, "UrlShortenerApi")
shorten_integration = aws_apigateway.LambdaIntegration(shorten_handler)
redirect_integration = aws_apigateway.LambdaIntegration(redirect_handler)

redirect = api.root.add_resource('{proxy}')
redirect_method = redirect.add_method("GET", redirect_integration)#鍵なし

shorten = api.root.add_resource("shorten")
shorten_method = shorten.add_method("GET", shorten_integration, api_key_required=True)#鍵あり

api_key = api.add_api_key('APIKey',api_key_name='BuildAPIKey')#鍵作成
plan = api.add_usage_plan('ForAPIKey', api_key=api_key, throttle={
            "rate_limit": 100,
            "burst_limit": 1000
        })#使用量プラン作成、鍵指定
plan.add_api_stage(stage=api.deployment_stage,
                           throttle=[
                               {
                                   "method": shorten_method,
                                   "throttle": {
                                       "rate_limit": 100,
                                       "burst_limit": 1000
                                   }
                               },
                               {
                                   "method": redirect_method,
                                   "throttle": {
                                       "rate_limit": 100,
                                       "burst_limit": 1000
                                   }
                               },
                           ]
                           )#ステージとプランの紐付け
self.map_company_subdomain('go', api)

```
元々のサンプルだとLambdaのmain関数で制御していたが、力不足でいい感じに制御できず（プロキシが任意のトークンを持っている場合にlambdaのmainが発火する為、適当な/token?targetUrl=の様な形でアクセスすると、鍵がついていない{proxy}ルートから短縮関数を発火させることができてしまう）、短縮する関数とリダイレクトする関数を別のLambda関数にした上で、それぞれを、APIGatewayのメソッドに紐付け、短縮関数に鍵をかけた。

# 短縮結果をJsonで受け取る
チュートリアルのままだと、　text/plainで返ってくる為jsonに変更

```python:lambda/shorten.py
return {
            'statusCode': 200,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps({'shrink_url': url})
        }
```

# まとめ
いちいちコンソールに確認しにいかなくてもよく、差分もcdk diffが便利なので積極的に使っていきたいが、土台となるAWSのリソースの知識が足りないと適切なコードは書けない。今回のコードもとりあえずは動くが、APIGatewayのステージ、スロットリング、鍵、Lamda関数の統合、制御などなど至らない点が多々あると思うので漸次改善していければと思った。