【最安値】AWSで独自ドメインの短縮URLを安く作る妙案【かもしれない】

# 概要

API Gatewayだけで短縮URLを作ります。

# Q. 短縮URLサービスをAWSで作る場合、どんなアーキテクチャにしますか？

## 1. API Gateway + Lambda + DynamoDB

サーバーレスのお手本構成です。DynamoDBを使いたくない場合はRDSも使えます。**RDS Proxy**の登場により、従来アンチパターンとされていた **Lambda + RDS** の構成が取りやすくなりました。

【参考】[【待望リリース！】もう Lambda×RDS は怖くない！LambdaでRDSプロキシを徹底的に検証してみた 〜全てがサーバレスになる〜](https://qiita.com/G-awa/items/b9138cc1c9e4867a905e)

Lambdaはコールドスタートでレスポンスが遅くなる場合がありますが、**Provisioned Concurrency** を使えば回避することができます（ただし別途お金がかかります）

【参考】[Lambdaのコールドスタートを改めて整理する](https://qiita.com/ny7760/items/700ae917da2c5b5e3f8a)

## 2. ELB + EC2 + RDS 

昔から愛されるスタンダード構成です。私も初めてAWSを触ったときはこの構成でRuby on Railsを動かしました。ただ最近の流行りはサーバーレスやコンテナかもしれません。

## 3. コンテナ

ECSまたはEKSになります。いずれもFargateかEC2を選べるので、実質4つの選択肢があります。諸説ありますが、**ECS on Fargate** がもっとも手軽だと個人的には思います。あとEKSは少々高いです（執筆時点で月72ドル〜）

【参考】[EKSは本当にECSより難しいのか？](https://dev.classmethod.jp/articles/ecs-and-eks/)

## 4. CloudFront + S3

いきなり意味不明ですが、そもそもアプリケーションは必要でしょうか？ 必要なURLの数だけ静的なWebページを作ればよくないですか？ WebページにJavaScriptを書けばリダイレクトできますよ？ HTTPステータスが200になりますケド。毎回HTMLファイルを作ってS3に置くのが面倒くさければ、CI/CDで自動化できなくもないですよ？...

自分ならやりません。HTTPステータス大事だし。

## 機能要件

ひとくちに「短縮URLサービス」と言っても色々な定義があると思います。アーキテクチャを考える前に、私が作りたいものの要件を以下にまとめます。

- 短縮URLの作成は自分（や限られたユーザー）のみが行う（＝誰でも短縮URLを作れるサービスではない）
- 短縮URLの作成はGUIでなくてもよいが、限りなくラクに行う
- すでに持っている独自ドメインを使う
- **めちゃくちゃ安くする**

※この要件に深い意味はなく、ただ「私が作りたいもの」というだけです

## で、このアーキテクチャを思いつきました

以下が本文です。

# API Gatewayだけで短縮URLを作る

API Gatewayには**モック統合**というものがあります。端的に言うとモックサーバーを簡単に作れる機能です。

【参考】[Amazon API Gateway でのモック統合による REST API の作成](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-getting-started-mock.html)

そしてふと思いつきました。モックで`301`を返せばリダイレクトできるのでは？と。

## 圧倒的安さ

AWSには12ヶ月の無料枠がありますが、それは含めず考えます。

記事執筆時点で、東京リージョンのAPI Gatewayの料金は次のとおりです（[公式](https://aws.amazon.com/jp/api-gateway/pricing/)）

> 最初の3億3300万リクエストまで　100万リクエストあたり　4.25ドル

なかなか安いです（別に外部へのデータ転送料金もかかりますが、今回はデータ量は限りなく少ないです）

そして**Lambdaやデータベースは使わない**ので、**その他の料金はかかりません。**

## APIを作ってみる

お試しなのでマネジメントコンソールで試します。

▼ REST APIを作成

![スクリーンショット 2020-12-09 2.18.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/13b71107-623a-ec73-a9fb-b3012751e326.png)

▼ エッジ最適化を選択

![スクリーンショット 2020-12-09 2.20.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/f9bdcbd3-7bea-8253-441e-4d51799ce2a4.png)

▼ APIが完成。さっそくリソースを作成

![スクリーンショット 2020-12-09 2.22.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/d7f9ac29-2a2e-cc35-725b-9bbe7ac37a4f.png)
![スクリーンショット 2020-12-09 2.23.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/63c93e11-2d46-d5cd-74f7-fab45e24b738.png)

▼ メソッドの作成。GETを作る。

![スクリーンショット 2020-12-09 2.23.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/139c65d5-507f-ddc7-c3ab-0ba1a535954d.png)
![スクリーンショット 2020-12-09 2.23.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/4f31810d-f661-2831-bfda-fb9bce939284.png)

▼ Mockを選択。

![スクリーンショット 2020-12-09 2.23.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/b9a04b95-bd7a-9d88-49d8-a9c484d01d33.png)

これで次の設定画面にたどり着きます。

## 詳解：モック統合

▼ この画面では4つの設定をします。①〜④の順に説明します。

![スクリーンショット 2020-12-09 2.37.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/df422b7e-07be-1623-f7f5-d2e162097142.png)

### ① メソッドリクエスト

クライアントから受け取るパラメータ・ヘッダーと、認可について設定します。**今回は編集しません**（誰でもURLにアクセスできるようにし、パラメータなしのGETメソッドにするからです）

### ② 統合リクエスト

**①メソッドリクエスト** の内容をどのようにバックエンドへ渡すか定義します。マッピングテンプレートを使うことで、①の内容を加工してバックエンドへ送ることができます（バックエンドには通常LambdaやAWSサービスが選べます。一つ前の手順でモック統合を選びましたが、それは「バックエンドをMockエンドポイントにした」ということです）

**重要なのは、モック統合の場合、ここで渡す内容がそのまま ④統合レスポンス に返ってくるということです**

デフォルトでは`Content-Type: application/json`のマッピングテンプレートに`{"statusCode": 200}`が入っています。**今回は編集せずそのまま使います**（ここを`301`にするやり方もあります。というかそれが正しいと思います）

### ③ メソッドレスポンス

クライアントに返すHTTPステータス・ヘッダー・コンテンツの組み合わせをここで定義します。例えばクライアントに`200`と`503`を返す場合は、その2つを定義しておきます。

**画面の矢印の向き的には ④統合レスポンス を先に設定したくなりますが、先にこれを設定する必要があります。**

▼ 今回はHTTPステータス`301`を`Location`ヘッダー付きで返したいので、以下のように設定します（すでにあった`200`は消しました）

![スクリーンショット 2020-12-09 3.32.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/daf80bea-f766-416e-fb6a-a00e5c4a98f3.png)


### ④ 統合レスポンス

ここが一番難しいです。ざっくり言えば **③メソッドレスポンス** と **バックエンドからのレスポンス** の対応表を定義します。例えばバックエンドから`504`が返ったときはクライアントに`503`を返すという設定をします。マッピングテンプレートを使うことで、バックエンドからのレスポンスを加工することもできます。

▼ 今回はバックエンド（＝モック）から`200`が返ってきた場合に`302`をクライアントに返すようにします。さらに「ヘッダーのマッピング」を使い、`Location`ヘッダーに`'http://abehiroshi.la.coocan.jp/'`を追加します（ちなみにクォーテーションがないとエラーになります）

![スクリーンショット 2020-12-09 4.34.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/b7a8cc40-b290-87c5-a2ee-d4a599b1f27f.png)

## テストしてみる

▼ 左上の「テスト」から試してみます。

![スクリーンショット 2020-12-09 2.37.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/b6ca73b8-17ee-50d5-ebbf-0c3a3e90036d.png)

▼ 期待通りのレスポンスが返ってきました。

![スクリーンショット 2020-12-09 3.42.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/7900a164-a591-1f6f-b912-0e58b670d6ef.png)

## デプロイする

▼ デプロイの手順はモック統合でないAPI Gatewayと同じです。デプロイしてURLを払い出します。スロットリングも設定しましょう。

![スクリーンショット 2020-12-09 3.45.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/7b10a856-36ff-fc34-c22f-d161ffe14572.png)
![スクリーンショット 2020-12-09 3.46.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/be6eadfd-157a-4116-63ce-fa6349e0a6bc.png)
![スクリーンショット 2020-12-09 3.48.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/f8bf1bd7-e076-87a6-cac4-97647f101d77.png)

これで`https://(略).amazonaws.com/v1/hiroshi`をブラウザ開くと、http://abehiroshi.la.coocan.jp/ に転送されるようになりました。

## 独自ドメインの設定

モック統合でないAPI Gatewayと同じなので割愛します。

【参考】[REST API のカスタムドメイン名を設定する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-custom-domains.html)

上記を設定することで`(独自ドメイン)/hiroshi`が使えるようになります（HTTPはHTTPSに転送されます）

**ここまでの手順で、1つの短縮URLを作成することができました。**

# 短縮URLの作成や管理をどうするか

次の問題は、短縮URLの作成や管理をどう行うかです。

一般的な短縮URL作成サービスでは、Webページ上で短縮URLの作成や管理を行います。当然このアーキテクチャではそれを実現できません。

前述の通り、短縮URLの作成はGUIでなくてもよいが限りなくラクに行いたいと考えていました。また、周り人もURLの作成をできるようにしたいと思っていました。

そこでGitHubリポジトリで短縮URLを管理し、CI/CDでAPI Gatewayを更新することにしました。つまり、短縮URL一覧のファイルを編集してGitHubにPushすると短縮URLが作られるようにしました。

*【次回】GitHub Actionsでデプロイする（追記: 書こうと思って、はやN年が経ちました...）*