個人開発で使った技術 ~バックエンド編~

個人開発で使った技術 ~バックエンド編~

2021/04/25

DEV

個人開発品のグルメ質問サービス、PiTARIのバックエンドを作るのに使った技術。

フロントエンドはこちら

AWS Amplify

バックエンドはAWS Amplifyを用いた。
Amplifyとは、AWSのリソースを簡単に組みわせて使えるサーバーレスフレームワーク。
AWS Black Belt Online Seminerでは、「Webフロントエンド、モバイルアプリの開発を加速さえるために作られたプラットフォーム」と定義されている。
イメージ的にはFirebaseのAWS版。
Amplify CLIをインストールすることで、コマンドライン上で対話的にバックエンドリソースを追加・開発・デプロイしていくことが出来る。

PiTARIは2者間で質問回答を行うサービス。
単純なCRUD処理と、マッチングの仕組みがあれば実現出来る。
またフロントはNuxt.jsでSPA構成だったので、バックエンドはできるだけシンプルに、且つ時間課金系のものは持ちたくない想いがあったので、Amplifyは最適だった。

Amplifyを用いた際のアプリ開発フロー

PiTARIの開発は、いわゆるスキーマ駆動の逆のような開発を行った。
まずサービスの見た目をデザイン→フロントを実装しながらGraphQLのスキーマを考る→Modelを作成してフロントとAPI接続。
開発フローが大切なのは、チームメンバーの手が止まる時間をなくし生産性を高く保つ為と思うが、個人開発は全部自分で作るのでその心配はあまり無い。
基本的には毎日作りたいところを作るみたいな感じだった。
特にAmplifyではフロントプロジェクト内に生成されるGraphQLスキーマファイルにモデル構造を記述し”Amplify push”コマンドを叩くだけで、バックエンドのデプロイが完了する。
(基本は開発初期にモデル設計はすべて行ったが、)これにより見た目を作りながら過不足はその場でモデルを修正してデプロイ、テストしてOKなら次の画面に、、といった感じで開発を進められる。
ページ毎にフロントとバックを同時に進められるので、1日の開発のマイルストーンも明確でテンポ良くすすめることができた。

API権限設定

Amplifyで作成するAPIでは、エンドポイントごと、またエンドポイント内のカラムごとに認証レベルを分けることが出来る。
例えば、通常のフロントアプリからのアクセスはCognito認証ユーザーのみにして、バックエンド間の連携はIAM認証と行った具合でアクセス権限を分けられる。
また、カラム毎に閲覧はPublic公開し、作成と編集はCognito認証ユーザーの且つデータのオーナーのみ、といった権限切り分けも可能だった。
PiTARIの場合、質問チケットはPublic公開、購入情報や質問回答内容はマッチングした2ユーザーにしか公開されないといった権限分けが発生したが、Appsyncの設定数行のコードで実現できてる。

検索

PiTARIの重要な機能の1つとして、質問チケットの検索機能がある。
探すお店の都道府県や詳細エリア、予算、ジャンル、回答者のSNSアカウント、その他諸々を指定してチケットを検索出来る必要がある。
一方でDynamoDBはキー・バリュー型であり、検索が得意でない。
ソートキーを多数設定してクエリする方法があるが、Amplifyでは何故かDynamoのソートコマンドの制限が多く、要件を実現させることができなかった。
Amplifyでは検索性を上げるため、対象のモデルに@seachable属性を与え(= Elastic Searchを導入)全文検索を追加する方法があり、開発で試したところ最高に便利だった。
しかし問題は、Elastic Searchは時間課金。。。一定時間は無料なのだが、2環境に入れると2倍になるため、あっという間に月あたり数千円の請求が発生していまい導入ボツとなった。。
そこでPiTARIでは少し強引だが、検索クエリは直接APIコールするのでなく、一度Lambdaを間に噛ませ、LambdaからAPIコールを行い全件を取得、Node.jsの自前コードで検索・ソートを行いレスポンスに返すことで検索機能を実現している。
今の所、速度も全然早いし、問題なく使えている。

認証

認証にはAWS Cognitoを用いた。
"amplify add auth"で、Cognitoリソースが割り当てられる。
サインアップ、ログイン、ログアウト、パスワード再発行など、一般的に必要な機能はすべてSDKで1コマンドで完了するので、技術的に特に工夫した点はない。

工夫点

Google認証をメインの認証方式とした。
Webアプリにあふれる中、このような零細アプリのためにIDパスワードを設定してもらうのは避けたいと思い、数クリックで完了できるOAuthに拘った。
事前登録を募った際も250名登録頂いた中で9割以上の人がGmailだったため、
ログイン画面では、Google認証ボタンを中央に大きく設置しできるだけOAuthに誘導し、IDPASSの認証はサブとして隅っこに配置した。

Function

バックエンドで行うロジックはすべてAWS Lambdaで実行している。
こちらも、"amplify add function"で簡単にLambda関数をセットアップ出来る。
DynamoDB streamingによるトリガー実行やCloud Watchの定期実行、Appsyncの接続やAuth連携もほぼ自動で作ってくれる神機能。

PiTARIで必要なバックエンドロジックは、

  • Stripeの決済処理
  • 質問回答のステート管理
  • ユーザーのスコアや売上計算
  • メール送信
  • 食べログのリンク取得

などであった。
これらをフロントのプロジェクト内で"add function"で作成したファイルに記述し”amplify push”するだけでLambldaへのデプロイが完了するので非常に効率的に開発できた。
質問が投稿された際のメール通知機能は、DynamoDB streamingでInsertを検知してFunctionをトリガ起動といった流れだが、Amplify CLIでセッティングするとテンプレートコードが入った状態で作ってくれるので、ほぼSendGridのサンプルコードをコピペのみでできてしまう。

工夫点

  1. PiTARIでは一部Webスクレイピングも行っており、こちらもAppsync経由で検索文字列をLambdaに引き渡し、LambdaからPythonでWebスクレイピングを行い、取得したデータをJSONに変換しレスポンスにわたす処理も行っている。
  2. NoSQL DBの課題の1つがトランザクション処理ができないということが言われているが、DynamoDBはトランザクションに対応している。かなり処理が重くなりDynamoのメリットが削がれるため、導入は最小限とすることが推奨されているが、PiTARIでも決済処理の箇所だけはデータの不整合は絶対避けたかった為、一部トランザクション処理を入れた。


Storage

画像保存はAmazon S3で行った。
こちらも、"amplify add storage"で1発だ。

決済、メール配信

決済はStripe、メール配信はSendGridを用いた。
どちらもほぼドキュメント通りの実装なので特に工夫点は無い。
Stripeはドキュメントがとてもわかりやすい。サポートチャットも英語でちょっとテンション高めだが、即レスでしっかり対応してくれてとても良かった。


Amplifyは簡単なようで、ブラックボックス化されているところもありエラーにはかなり苦しんだ。特にCloud Formationが壊れると手に負えなく、一度環境ごと削除といった自体も何度か発生した。
1環境壊れてもバックアップを常に持ってmasterを死守する仕組みを用意しておく必要がある。

Amplifyに関しては、細かい設定やそれによって実現出来ることが多数あり書きたいことが無数にありまとまらないのだが、
今回は概要ということで、細かい話は別途小出しで記事にしていきたい。