Next.jsをサーバーレスでやっていくためのServerless Next.js Component

今回は最近その存在感がますます上がっているNext.jsとサーバーレスの話です。

はじめに

この投稿は2020年11月27の21時から開催予定のイベント(ライブストリーミング)で話す内容です。

serverless-newworld.connpass.com

もし間に合えば、かつ時間があればぜひライブ配信のほうにも参加ください。

さて、今回は11/9に登壇させていただいたFront-End Studyでの話でも少し紹介したServerless Next.js Componentについて取り上げます。

僕は昨今のフロントエンドWeb周りの技術では最近は一番Next.jsが好きなんですが、そのNext.jsで作ったアプリケーションをAWSのサーバーレスで動かしていくって話です。Static Site Generation(SSG)したものをS3などで配信していくというのはよくある話ですが、もちろんServer Side Rendering(SSR)も可能です。

さて、SSRとサーバーレスと言えば過去に一度やっていて、そのときはNuxt.jsを使って説明しました。そのあたりはこちらにまとまっています。

www.keisuke69.net

前回のNuxt.jsの際にも言及したように、aws-serverless-expressを使ってサーバーレスでSSRやるのは簡単ではあるものの、一方で無駄も多いです。そこで、本来であればその無駄を最適化するために自分で手を動かしていく必要があったりするのですが、Next.jsを使っている場合はServerless FrameworkのプラグインであるServerless Next.js Componentを使うとこの辺をうまいことやってくれます。しかもちょっとやるだけなら超絶簡単

というわけでやっていきます。今回の環境は以下を前提とします。

Lambdaのランタイム: Node.js 12.x
Next.js 10.0.3

では早速やっていきます。

(2020.11.27 Update)

上記ライブ配信アーカイブが公開されています。3:25あたりから始まります。

https://www.youtube.com/watch?v=r0OdiKoMmig


サーバーレスアンチパターン今昔物語 第七夜

チャンネル登録してくれると更新通知も飛ぶのでどうぞ。

サンプルアプリ

今回もデプロイするためのサンプルアプリを用意しています。ソースコード一式はこちらにおいておきました。

Nuxt.jsのときと同様に簡単なブログです。コンテンツについても前回同様はてなブログでやっている別のブログWordPressにインポートしてWordPressAPIを叩く感じになっています。

使うWordPress自体は今回は新たに作成せずに前回作ったものがまだWordPress.com上で動いていますのでそれをそのまま使っています。

Next.jsで作ったアプリそのものについてもNuxt.jsのときと同様に大したものではないので詳細は割愛します。構成としては投稿をリストするトップページとそのページからリンククリックで遷移する個別のページです。URLとしては以下のような感じです。

  • / - トップページ兼一覧ページです。個別の記事へのリンクを一覧表示します。(SSR
  • /posts/[id] - 個別の投稿ページです(SSG)
  • /about - SSGとSSRの確認テスト用

今回は全部SSGでもいいようなアプリケーションなのですが、それでは話が微妙なのでトップページの/SSRで処理しています。あと、実際のところSSGとSSRのどっちで動いてんのかわからん!とか挙動を確認してみたい!って目的のために/aboutというページを用意しています。残りは全てSSGです。

さて、以前にSSRをLambdaでやったときには大きく以下のような手順を踏むという話をしました。

  1. Nuxt.js / Next.jsで作ったアプリをExpressで動かす
  2. Expressで作ったアプリをLambdaで動かす

今回はこういった手順を踏まずにServerless Next.js Componentでやっていきます。

ちなみにこのアプリケーションをyarn buildするとこんな感じになっています。 f:id:Keisuke69:20201127082318p:plain

これを見ておわかりのように//api/helloSSRされ、/posts/以下はSSGで各IDごとにPre-Renderingされています。

Serverless Next.js Component

AWSを始めとするサーバーレス環境のデプロイやプロビジョニングツールであるServerless Frameworkの関連機能であるServerless Componentの一つです。 Serverless Componentっていうのは超絶簡単に説明するとServerless Frameworkという仕組みを通じてユースケースを意識したServerless Applicationをデプロイできるというものですが、正直に言うとあまり詳しく知らないです。 今回利用するServerless Next.js ComponentはNext.jsをサーバーレス環境にいい感じでデプロイしてくれるServerless Componentです。

github.com

このServerless Next.js Componentの特徴としては、ざっくり言うとNext.jsのSSRをLambda@Edgeで実現してくれるというものです。Nuxt.jsでやったときのようにaws-serverless-expressを使ってAmazon API GatewayAWS Lambdaを使って実現するのではなく、AWSCDNであるCloudFrontのエッジサーバ上で稼働するLambda@Edgeを利用します。

また、aws-serverless-expressでやる場合、そのままだとリソースの観点で無駄が多いという話をしましたが、Serverless Next.js Componentでデプロイすると静的なアセットや静的ファイルとして生成されたものはS3でホスティングするようにデプロイした上でCloudFrontを使って配信されるようになります。

また、Lambda@Edgeで静的ページへのリクエストをハンドリングしてSSRしないリクエストはS3へとフォワードしてくれます。またいわゆるルーティングもやってくれるんですが、Dynamic Routingもサポートされています。

こういったことを、Next.jsが標榜するのと同じZero Configでできます。まじでめっちゃ簡単です。しかも今回はCloudFormationベースでのdeployではないこともあってデプロイ自体が速いですね。

さて、Lambda@Edgeを使うってこともさることながら、静的アセットや静的ファイルのみS3とCloudFrontを使ってサービスするというのは口で言うのは簡単ですが想像以上に面倒だと思います。プロダクション用にビルドしてできあがったもののうち、静的なもののみS3にデプロイするってのはCI/CDのようなパイプラインで考慮する必要があり多少作り込む必要もあります。ビルドしたものを何も考えずにごそっとデプロイするってわけにはいかないんですね。

また、Lambda@EdgeでルーティングやSSG、SSRのハンドリングといったことを自前で実装するのは結構たいへんです。

このあたりをまるっとやってくれるServerless Next.js ComponentはNext.jsを使ってる人にはとてもいいものだと思います。というかNext.jsをサーバーレスでSSRやるにはこれ一択と言ってもいいんじゃないかなと思ってます、個人的には。

デプロイ

実際のところ前回やったNuxt.jsのときのようにサーバーレスで動かすために実装しなければいけないこと、設定とかを特に必要としません。今回のサンプルアプリも実際には普通のサーバーとかでも動きます。

Serverless Frameworkベースなのでserverless.yamlを用意します。クレデンシャルなどのServerless Framework使う上で必要な設定はやっておいてください。serverless.yamlの中身は以下のように。これはデフォルトでカスタムドメインはCloudFront周りの設定などもう少し細かく設定することも可能ですが、これだけでも十分問題なく動きます。まさにZero Config。

myNextApplication:
  component: "@sls-next/serverless-component@1.18.0"

myNextApplicationの部分は好きに定義して大丈夫です。一点注意があるとするとバージョン番号(今回であれば1.18.0)を指定すること、そして必ず最新版を入れたほうがいいです。使い始めてからもなるべく最新版を使い続けるようにしたほうがいいです。自分の経験では少し古いバージョンでも動かないことがありました。

あとは同じServerless FrameworkといってもこちらはComponentということもあってかオリジナルとは微妙にフォーマットが違うそうです。

ファイルを用意したらシンプルに、

$ serverless

と実行してください。通常のServerless Frameworkを使うときのようにserverless deployやsls deployではなくserverless`とだけ実行してください。実行完了までにかかる時間は恐らく生成されるページの数などによって左右されると思います。また初回実行時はCloudFrontのディストリビューション作成を伴うため、有効になるまで少し時間がかかります。

実行が完了するとこんな出力がされます。

serverless

  myNextApplication: 
    appUrl:         https://<abcdefghijklm>.cloudfront.net
    bucketName:     abcdfeg-abcdefg
    distributionId: XXXXXXXXXXXXX

  ******************************************************************************************************************************************************************
  Warning: You are using the beta version of Serverless Components. Please migrate to the GA version for enhanced features: https://github.com/serverless/components
  ******************************************************************************************************************************************************************


  162s › myNextApplication › done

このときは初回だったので162秒かかったようです。ここにあるappUrlというのがCloudFrontで配信されているエンドポイントですね。なのでこちらにブラウザでアクセスしていきます。なお、カスタムドメインを利用することももちろん可能です。

今回はブログということで全部SSGにしてしまっていたのですが、無理やりindexのページだけSSRをするようにしています。

なのでアクセスしてみるとわかるのですが各ページへのアクセスは速いです。一方でトップページへのアクセスはSSRが実行されているので他と比べると多少遅いはずです。

なお、ドキュメントではgetInitialPropsを使っている場合にSSRするとなっていますが、9.3以降で推奨のgetServerSidePropsにも対応しています。もちろんgetStaticPropsgetStaticPathsにも対応。

また、serverlessを実行すると以下の内容のnext.config.jsが一時的に生成されます。

module.exports = { target: 'serverless' };

注目はターゲットがserverlessであるということです。これは何かというとNext.js8からサポートされたもので、ページごとに出力します。このファイルは実行完了時に自動的に削除されます。

作成されるリソース

実際にデプロイすると以下のようなリソースが作成されています。御覧のようにサーバーレスでWebっていうと出てくることが多いAmazon API Gatewayの姿がありません。

CloudFrontのディストリビューション

オリジンとして一緒に作成されたS3のバケットが指定されています。バケットというかS3でStatic Website Hostingした際のURL。そしてOAIが設定されているのでCloudFront経由のリクエストしか届かないようにも設定されています。 特徴的なのがBehaviorの設定ですね。こんな感じになってます。

f:id:Keisuke69:20201127015136p:plain

これってつまりNext.jsをビルドしたときに生成されるディレクトリやそこへのパスを元にリクエストの振り分け設定がされています。具体的には静的なファイルなど基本的にすべてS3を使ったオリジンが指定されていますね。

Lambdaファンクション

Lambdaのほうはたった2つのファンクションがデプロイされているだけです。

defaultとapi用でどちらも512MBメモリです。

これらのファンクションがLambda@Edgeとして実行されるように設定されています。

S3バケット

S3はこんな感じで、静的Webサイトとして配信するための設定がされています。 f:id:Keisuke69:20201127020233p:plain

大まかな挙動

Serverless Next.js Componentを使ってデプロイした際の挙動について簡単に解説します。基本的にはビルド結果の生成物をS3に種別ごとにフォルダをわけて格納し、リクエストのパスを元に振り分けるという感じになります。その振り分けを行うのにCloudFront上でのBehaviorとLambda@Edgeが使われます。

まず、Behaviorについて見ていきます。作成されるのはDefalutとそれ以外の4種類で計5種類です。Behaviorというのは指定したリクエストパスのパターンでオリジンを変更したり異なる設定をしたりするもので、それが5パターンあると思っていただくといいです。それが以下の5種類です。

  • _next/static/*
  • static/*
  • api/*
  • _next/data/*
  • Default(*)

_next/static/*static/*は来たリクエストをそのままS3へと転送します。

api/*Origin Requestイベントに対してAPI用のLambda関数が設定されています。Origin RequestイベントってのはCloudFrontがリクエストをOriginに転送したときにのみ関数が実行され、CloudFrontのキャッシュから返される場合は実行されないというものです。ここでAPIへのリクエストをハンドリングします。ここでいうAPIってのはNext.jsで作成可能なAPIのことですね。

Next.jsではサーバーサイドで実行するAPIのエンドポイントも作成可能になっています。これはpages/api配下にファンクションを作ることで実現するのですが、これをいわゆるサーバーレス環境のファンクションとしてデプロイ可能となっています。ただし、あくまでも汎用的なファンクションシグネチャとなっていてLambdaそのもののフォーマットとは少し異なります。

こんな感じのものを用意します。

export default (req, res) => {
  // ...
}

これをyarn buildすると.next/serverless/pages/api/の下にビルドされたものができあがるのですが、Serverless Next.js ComponentはこれをLambdaファンクションのデプロイパッケージに含める形でデプロイされます。なお、LambdaファンクションとしてはServerless Next.js Componentが生成するindex.jsが登録されていて、APIへのリクエストに応じて呼び出す形になります。実際に登録されるindex.jsは御自身の開発環境のプロジェクトフォルダ内の.serverless_nextjs/api-lambda/index.jsです。

_next/data/*もLambda関数が紐付けられています。こちらはOrigin RequestOrigin Responseの両方でどちらも同じLambda関数で処理されています。Origin ResponseはOriginからのレスポンスを受け取ってキャッシュする前に実行されるというものです。Originからエラーが返ってきても実行されますが、Origin Requestで実行された関数からレスポンスが生成された場合は呼び出されません。

ここでは3種類のリクエストをハンドリングします。ここでいう3種類とは、

  • サーバーサイドレンダリング
    • getInitialPropsやgetServerSidePropsが使われているページはこの時点でレンダーされ、すぐにレスポンスされます
  • 最適化された静的ページ
    • Next.jsによってプリコンパイルされたHTMLページへのリクエストはS3に転送されます
  • パブリックリソース
    • いわゆるルートレベルの静的なアセットたちですね。faviconとかの。これらもS3へと転送されます。

静的ページとパブリックリソースもLambdaファンクション通るのはなんで?って思ったかもしれませんがこれはそのルートがパスのパターンマッチングで特定できないからだそうですがちょっと深堀りできていないです。

最後のDefaultも_next/data/*と同様のようです。同じ関数、同じイベントが設定されています。

つまり、上から順にリクエストされたURLのパスを評価していって、マッチしたパターンがあればオリジンにそのまま転送したりしていくのです。

なお、デプロイごとにCacheのInvalidateも行われます。これは/*を対象に行われます。

できないこと

さて、こんな感じで割と簡単に利用できるServerless Next.js Componentなんですが、実は最近のNext.jsにおける最注目機能といっても過言ではないIncremental Static Regeneration(ISR)には対応していません。ISRとはすごく雑に説明すると『基本的にはキャッシュから返しつつ、有効期限を過ぎたら非同期でキャッシュの更新をSSRで行う』っていうものです。Cache Controlにおけるstale-while-revalidateと同じような考え方が適用されたものと言ってもいいかもです。Next.js 9.4から追加された機能です。

これの嬉しさについては別のサイトに譲るとして、SSGとSSRそれぞれ辛みがあるわけですがその辛みの一旦を解消してくれそうな革命的な機能です。つまり、これまでパフォーマンス的に、スケーラビリティ的に、運用的にはSSGのほうが楽ではあるものの、SSRでないと動的な処理などは実現できないことがあったわけです。一方でSSGはその性質上ページの数やコンテンツの数が多くなるとビルドが思くなるという課題もありました。それをISRでは言葉どおり段階的にしつつ、CDNのキャッシュを有効活用しつつ、動的に近いこともできるようになった感じですね(実際には動的ではないですが)。

とういわけでこれに対応されるととてもいいのですが、残念ながら現状ではまだ対応されていません。一応、Issueは起こされているので近い将来対応することが期待されます。。。

まとめ

今回はNext.jsのアプリケーションをAWSのサーバーレス環境でServer Side Renderingしていく上で非常に簡単に使えるServerless Next.js Componentの紹介でした。とても使い勝手はいいのですが、破壊的なアップデートもガンガン入ってたりするのでそのあたり気をつけつつ使ってもらえるといいんじゃないでしょうか。

きっとNuxt.js向けの似たようなものもそのうち出てくると期待。

Monthly AWS Serverless Update 202010

2020年10月のサーバーレス関連まとめです。こちらのイベントで取り上げる内容です。

serverless-newworld.connpass.com

2020/11/05 Update 配信のアーカイブがあがっています。


Monthly AWS Serverless update 2020/11

2020年10月のリリース

10月は割と大きめのアップデートがありました。

AWS AppSync が AWS WAF のサポートを追加

  • 待望の人も多いんじゃないでしょうか。AWS AppSyncがAWS WAFに対応しました。少し前に質問でもいただいてましたね
  • これまでWAFを使うのが難しかったので泣く泣くAppSyncを諦めてた人には朗報かと思います

Amazon SNS を使用して SMS を送信するアプリケーションが新しい 5 つのリージョンでホスティング可能になりました。

AWS Lambda の拡張機能: Lambda を運用ツールと統合する新しい方法 (プレビュー)

  • これも嬉しい人多いのではないでしょうか
  • 日本語ブログだと拡張機能になってますがいわゆるAWS Lambda Extensionsのことです
  • サードパーティを含む各種運用ツールを統合しやすくなったというもので、現時点ではサードパーティだとAppDynamics、Check Point、Datadog、Dynatrace、Epsagon、HashiCorp、Lumigo、New Relic、Thundra、SplunkあたりのExtensionが使えるようです
  • これまでは難しかったLambdaのライフサイクルに深く関与することが可能になりました。ブログの内容も書き換える必要がある
  • プレビューです

Amazon CloudWatch Lambda Insights の発表 (プレビュー)

AWS Lambda が、AWS PrivateLink のサポートを開始

  • これは金融系のシステムなどより強固なセキュリティを必要とするユーザには待望の機能かもしれません
  • VPC内からインターネットを介さずにLambda関数を呼び出すことができるようになります
    • たまに誤解している方がいるのですが、Lambda関数をVPCアクセスできるようにしてもLambda関数の実行自体はユーザのVPCではなくサービスのVPCで行われたままです。つまり実行呼び出しはインターネット越しにAPIを呼び出すしかなかった
  • 先月のAPI GatewayのMutual TLS対応と合わせてよりサーバーレス採用しやすくなってきたのではないでしょうか

Amplify プロジェクトに既存の Cognito ユーザープールと ID プールを使用する

  • 嬉しい人多そう

Amazon SNS は、メッセージの厳密な順序付けと重複排除が行える先入れ先出し (FIFO) トピックを導入

  • Amazon Simple Notification Service (SNS)でもFIFOをサポート
  • このアップデートは個人的にはちょっと驚いた

AWS Step Functions が Amazon Athena サービスとの統合サポートを開始

Amazon SNS で、SMS メッセージを送信する際に発信番号の選択をサポートできるようになりました

Amazon API Gateway は、デフォルトの REST API エンドポイントの無効化をサポートするように

  • これは何が嬉しいかっていうと、これまではAPI GatewayのRESTでAPIを公開していた場合、カスタムドメインを使っていてもデフォルトで用意されたエンドポイントが利用できたままだったが、それを閉じられるということ
  • つまり、設定したカスタムドメインへのアクセスだけに制限できるということ

Amazon Cognito ユーザープールにより、クォータ管理と使用状況の追跡が容易に

その他ブログなど

Introducing AWS Lambda Extensions – In preview | AWS Compute Blog

  • Lambda Extensionsに関するより詳細な内容が書かれています。英語です

AWS SAM CLIを使ったLambdaのローカル実行と簡単デプロイ - BASEプロダクトチームブログ

  • SAMの紹介もさることながら、ポイントはBASEさんで実際に行っている運用設定の一部が紹介されている

白いマスクから怪人マスクへ、AWSでサーバレスLINE写真処理アプリの開発記 - Qiita

  • Amazon Rekognitionとの組み合わせ

SODANE - AWSと各種SaaSを活用したサーバレス・ライブコマースシステムの開発

  • 北海道テレビさんの水曜どうでしょうエアキャラバンin 赤平2020のオンラインイベント会場の事例
  • 驚くべきはサーバーサイドの担当者、インフラ担当者がおらず、プログラミングにもそれほど明るくない状態からこれを立ち上げてサービスインしているところ
  • これはうかうかしていられない

API Gateway 開発者に読んでほしい、意味がわかると便利な実行ログ - Qiita

  • ぶっちゃけAPI Gatewayのログ周りって難しいところあると思うのですがそのあたりが解説されています

サーバーレスのプラクティスをシンプルに実現する / AWS Lambda Powertools - Speaker Deck

  • AWSJのサーバーレスSA下川さんによるLambdaの便利ツール紹介

サーバーレスアーキテクチャを採用する際のコストに対する重要な考え方 | Serverless Operations

Misreading Chat で "From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers" について話した - blog.8-p.info

AWS Amplify 実践編 #AWSDevDay / Practical AWS Amplify at AWS Dev Day 2020 - Speaker Deck

  • AWS Amplifyでこういうことできないかな?っていうような話が詰まってます

分散システムにおけるSagaパターンのAWS Step Functions による実装 #AWSDevDay - Speaker Deck

  • 以前のPodcastに福井さんがゲストで出ていただいたときに話していたSagaパターンをAWS StepFunctionsを使ってどうやって実装するかって話
  • Sagaパターンそのもの解説もある

Lambda Extensionに対応した、New Relic サーバーレスモニタリングを試してみた | Developers.IO

  • Lambda Extensionsで使えるNew Relicを試してみた話
  • クラメソブログですが書いているのはNew Relicの人っぽい

localstack有料版をちゃんと使ってみる - 続 カッコの付け方

  • AWSの各サービスのAPIを叩く処理を開発するときにローカルで使えるモックサーバのようなものとしてlocalstackというのがあるんですが、それの有料版の話です
  • このlocalstack、元はAttrasianが提供していたんだけど今見ると違うのかな?ちょっとわからないです
  • 有償版は無償版と比べると使えるサービスの数だったりLambda Layerをサポートしてたり認証周りが使えたりってのがあるみたいです

The Ultimate Guide to AWS Step Functions | Dashbird

  • Ultimate GuideというだけあってかなりボリューミーなAWS StepFunctionsに関する解説記事。英語です

サーバレス革命はなぜ行き詰ったのか

  • まだ道半ばなので行き詰まっているとは思わないけどノーコメントでw

個人開発・スタートアップで採用すべき最強のアーキテクチャを考えた - Qiita

  • ちょっと話題になりましたね
  • 個人的にはあくまでも全部AWSで作らならって感じだとは思います

Jamstack とサーバーレスで提供する「大阪都構想」特設サイトの舞台裏 - JX通信社エンジニアブログ

  • つい先日行われた大阪都構想住民投票についての特設サイトを作った話
    • NewsDigestの特設サイトの裏側だそうです
  • Next.jsを使っていて、Serverless Frameworkのプラグインであるserverless-next.jsを使ってます
  • OSSなども活用することで開発開始から1週間半程度でリリースしたそうです
    • Headless CMSはメイン開発者の人がOSSで開発しているものを採用したとのことですが、それでも速い

AWS Amplifyを無理して使わなくてもいいケースについて挙げてみる

AWS AmplifyといえばAWSが提供しているフロントエンド開発者向けのライブラリやツールセットです。今回はそんなAWS Amplifyについてです。

はじめに

この投稿は2020年10月22の21時から開催予定のイベント(ライブストリーミング)で話す内容です。

serverless-newworld.connpass.com

もし間に合えば、かつ時間があればぜひライブ配信のほうにも参加ください。

あと、今回Amplifyを取り上げる理由は、以前このシリーズで取り上げるテーマとして何がいいかっていうアンケートを取った結果です。

AWS Amplifyのウリだったりいいところってのはこれまでにいろんなところで語られていると思うのでそちらにお譲りするとして、サーバーレス同様に現実世界の課題すべてを1つのソリューションだけで解決しようとするのはあまりおすすめしないよねってことで、そんな観点でまとめます。

(2020.10.22 update)

上記イベントのアーカイブを公開しています。


サーバーレスアンチパターン今昔物語 第六夜

チャンネル登録してくれると更新通知も飛ぶのでよろしくです。

おさらい

そうは言っても、地ならしがてら簡単にAmplifyの特徴についてまとめておきます。

まず、AWS Amplifyと一言で言っても、いくつかの構成要素がありましてAmplify Library、Amplify UI Component、Amplify CLI、Amplify Consoleから構成されます。LibraryとUI Componentは区別していないこともありますが、わかりやすく言うとフロントエンドアプリケーションの開発に使うもの、バックエンドの構築・設定に使うもの、ビルド/デプロイ/ホスティングに使うものにわかれてると捉えてもらうといいでしょうか。

で、その特徴としてもいろいろあげられていますが、とても乱暴な表現であることをあえて承知で言いますと、

バックエンドにAWSを使ったフロントエンドアプリケーションを(全て手動でやるよりは比較的)手軽に開発するためのもの

と言えるでしょう。この説明に多方面からツッコミが入りそうな気はするが今回の本題ではないので気にしない。

フロントエンドアプリケーションと言っているとおり、JavaScript / iOS / Androidあたりに対応しています。JSについてはReact/Vue/Angularに加えて、ReactNativeとIONICに対応しています。今をときめくFlutterについてもDeveloper Previewとして対応されています。あ、最近Server Side Rendering(SSR)にも対応しました。その他、細かい特徴とかは公式のドキュメントやAWSがやってるセミナーの動画とかを見てもらうととても詳しく説明されています。

ちなみに今回取り上げるのは主にLibraryとCLIに関連する話、つまりAmplifyを使った開発そのものに関わる話。デプロイやホスティングをしてくれるAmplify Consoleに関してはほとんど触れません。というか、Amplify ConsoleについてはSPA(Single Page Application)やSSG(Static Site Generation)のアプリケーションをAWSで簡単にGitを中心としたデリバリパイプラインを作って公開するにはとてもいいものだと思う。Jamstackなんかはビルド9割だと思っていて、そのくらいコンテンツの更新からビルドおよびデリバリのパイプラインが重要だと思うのでそういうときにAmplify Consoleは便利だと思う。

AWSアカウントが必要ってのはあるけど持ってるならばとてもいいと思います。僕も個人的な用途で使っています。

AWS Amplifyはフロントエンド開発者にとっての銀の弾なのか

はい、もちろん違います。そもそも前述のとおりバックエンドをAWSで開発していない限り嬉しさも少ないです。というかゼロだと思います。あくまでもバックエンドにAWSがいる前提で使うもの。

自分が話をすることの多いサーバーレス関連でもよく言ってるのですが『最大のアンチパターンはすべてをサーバーレスにすることにこだわること』と言っています。

そしてこれはあらゆるものに当てはまると思っています。使い古された言葉ですが銀の弾などないのです。もちろんAWS Amplifyについても同様です。

その上でAWS Amplifyについて言うなれば、先ほども言ったとおりAWS Amplifyはあくまでも手動でもできることを比較的手軽にやれるようにしてくれているものです。フロントエンド開発者フレンドリーに。

つまり、言い換えるとAWS Amplifyじゃないとできないことは正直なところほとんどありません。

さらに言うとAWS Amplifyで対応していたとしても実際のところAmplify使ってやるより別の方法のほうがいいんじゃない?って思う点も個人的にはいくつかあります。

無理して使わなくてもいいケース

バックエンド側のライフサイクルや開発部隊が分かれてる場合

先ほども言ったようにAmplifyのメリットはフロントエンドから見たときにバックエンドを簡単に扱えることがメリットです。なのでこのケースの場合はAmplify使うメリットが薄まると思っています。

Amplifyを使う場合ってバックエンドはAmazon API Gateway + AWS Lambdaを使ったサーバーレスなREST APIAWS AppSyncを使ったGraphQL、もしくはAmazon S3などのようにAWSサービスやそのリソースをAPI経由で直接操作するパターンかと思います。

なかでも、REST APIを作る場合においてバックエンド側のライフサイクルや開発部隊が分かれている場合、フロントエンド側がバックエンドをいじることは少ないですよね。つまり、フロントエンドはバックエンドAPI作らないし、バックエンド側にとっても提供するものはAPIまでになるのでクライアントライブラリであるAmplifyは不要です。

また、これはあくまでも個人的な意見ですが、仮にフロントエンド開発者がREST APIまで作る場合だとしてもサーバーレスなリソースのプロビジョニングやデプロイを行うのはAWS SAMであったり最近だとAWS CDK使うほうが圧倒的にいいと思っています。餅は餅屋ということです。その中でも個人的にはAWS謹製ではないですがServerless Frameworkが好みです。理由はプラグイン機構とそのエコシステム、これに尽きる。

なぜ、Amplifyではなくこれらのほうがいいかというと、amplify add apiREST APIを作成するとLambda関数周りはこんな感じで生成されます。

amplify/backend/function/
|-- serverlessnewworld20d765121f
|   |-- amplify.state
|   |-- dist
|   |   `-- latest-build.zip
|   |-- function-parameters.json
|   |-- parameters.json
|   |-- serverlessnewworld20d765121f-cloudformation-template.json
|   `-- src
|       |-- event.json
|       |-- index.js
|       |-- package-lock.json
|       `-- package.json
`-- serverlessnewworld20e0fe6255
    |-- amplify.state
    |-- function-parameters.json
    |-- parameters.json
    |-- serverlessnewworld20e0fe6255-cloudformation-template.json
    `-- src
        |-- event.json
        |-- index.js
        `-- package.json

amplify pushすると作成したリソースをAWS上に反映するんですが、その際Lambda関数はデプロイパッケージにまとめられます。このツリーではamplify/backend/function/serverlessnewworld20d765121f/dist/latest-build.zipがそれです。これが関数ごとにできます。残念なのはこれを個別にデプロイできないんですよね。

また、API Gateway + Lambdaで完結するならまだいいですが大抵の場合はLambdaからアクセスする別のリソースが存在していることも多いです。そういったものについてはAmplifyの対象から外れてしまうんですよね。後述するやり方でできなくもないですが、それだったらシンプルにCDKやサーバーレス系のデプロイツールを使ったほうがいいと思います。

AWSのリソースに直接アクセスしない場合

フロントエンド側からAWSリソースに直接アクセスするケースっていろいろあるとは思いますが、多いのはAmazon CognitoとAmazon S3を利用する場合なのではないかと思います。

さて、ここでAWSリソースに直接アクセスするときのAmplifyの利点について簡単にお話すると、大きく2つの観点があります。1つはAmplifyがサポートしているものであればソースコードの記述がシンプルになるということ。もう1つはそれらリソースへのAmazon CognitoおよびAmazon Identity and Access Management (IAM)を使った認可が簡単に扱えるということです。

実際に以下は公式に載っているサンプルですがS3へのファイルアップロードだとこんな感じにかけます。

import { Storage } from 'aws-amplify';

~~

Storage.put('test.txt', 'Hello')
    .then (result => console.log(result))
    .catch(err => console.log(err));

とてもシンプルです。これだけでS3に対してファイルをpublicにアップロードできます。同じことを普通に書いたらこんな感じです。実際に動かして試してないのであくまでもイメージってことでお願いします。

var upload = new AWS.S3.ManagedUpload({
  params: {
    Bucket: sample,
    Key: "test.txt",
    Body: "Hello",
    ACL: "public-read",
  },
});

var promise = upload.promise();

promise.then(
  function (data) {
    console.log(data);
  },
  function (err) {
    console.log(err);
  }
);

簡単さがわかると思います。このあたりはまさにAmplifyの存在意義を示していると思います。バックエンドのAWSリソースをフロントエンド開発者フレンドリーな形で使いやすくなってますね。

一方、裏を返すとこの2点が不要な場合はAmplifyも不要かもしれません。

認証・認可の手段としてCognito使わない場合

よくAmplifyのサンプルや紹介でCognitoを利用したAuthの実装が簡単だというものを見たことがあるかと思います。確かに、Cognitoで認証する場合は、Cognitoの設定がCLIから簡単にできるだけでなく認証画面も簡単に作れます。また、前述のとおりAWSの認可の仕組みをそのままアプリケーションからも利用したい場合はAmplifyという選択はとてもいいと思います。

一方で、そもそも認証手段としてCognito以外を使いたいケースも多いのではないでしょうか。そしてIAMベースの認可が不要な場合。この場合はもちろんAmplifyは不要です。

そして、認証は別でやりたいけどIAMベースの認可はしたい。そんなときCognitoにはSAML連携やOIDC対応の外部プロバイダ、あとは主要なソーシャルログインプロバイダ(FacebookGoogleなど)と認証部分を連携させることができます。

でもAmplify CLIからこれらの設定がすべてサポートされているわけではないんですね。例えばSAMLやOIDCのプロバイダ、例えば認証にAuth0を使うといったフェデレーションの設定はAmplify CLIからはできないため結局、マネージメントコンソールからの作業が必要になります。
※ソーシャルログインのフェデレーションは設定できます

Server Side Rendering(SSR)の場合

まず、Amplify Consoleの話から。AmplifyがSSRに対応したといってもLibrary側の話であってAuthにおけるLocal Storage依存をやめたとかそういう話です。Amplify ConsoleでSSRもホストできるようになったというわけではないです。つまり、Amplify Consoleでは依然としてSSRを必要とするサイトはホスティングできない。

そしてLibrary側の話をすると、Next.jsとかを使ってSSRしたいときに普通に使えるよってことです。ただ、公式のドキュメントとかではデプロイをamplify pushではなくてServerles FrameworkのServerless Next.js Componentを使ってる。だったら別にAmplify要らないかなーと思ったけど、そこは結局前述のAWSのリソース使いたい場合とかと一緒ですね。

AWS WAF使いたい場合

これもAmplify Consoleの場合の話です。Amplify Consoleの静的サイトのホスティングは、コンテンツ配信にCDNであるCloudFrontを自動でセットアップしてくれます。だがしかし、ここで作られるCloudFrontのリソースはユーザには見えないのです。つまりサービス側が所有するリソースとして作られているのです。というわけで何らかの理由でCloudFrontと連携させる形でAWS WAFを使いたいとなっても現時点ではできない。あとはCloudFrontの細かい設定をしたい場合なども。

代わりにamplify publishを使えばユーザのリソースとしてCloudFrontとS3がセットアップされますが、AWS WAFを使う場合は別途設定が必要です。

Amplify CLIに対応していないバックエンドを利用したい場合

一応このような場合でも、Cloudformationのテンプレートを自分で用意してカスタムカテゴリとして登録するって方法があるにはあります。なので基本的にはAmplifyの機能を使いつつそこだけ自分で用意するってことができます。

でも、自分でCloudformationのテンプレート用意する時点でAmplifyの利点は失われているとも言えます。それを用意するならCDKとか使ったほうが楽だなと僕なら思ってしまいます。もちろん、そのボリューム感見合いの話ではあるのですが。

AWS Amplifyがおすすめのケース

最後にAmplifyがおすすめのケースについても簡単にお話しておきます。基本的にはこれまでの話の裏返しなんですが、ここまで触れてこなかったパターンが1つあります。

それは、

フロントエンド開発者がAppSyncを使ってGraphQLでシンプルなアプリを開発する場合

です。このケースの場合はAmplify使うのを強くおすすめします。とても良い体験が得られると思います。

これに関してはいろんなところでサンプルやドキュメントがあるのでそちらを参照してください。

一方でシンプルと言っているのには理由があります。それはAmplifyからAppSyncを使う場合に利用できる@connectionディレクティブです。これを使うとDynamoDBにも関わらずRDBMSのようなリレーションを表現できます。実際にはリレーションを表現するためのリゾルバが自動で生成されるのと、多対多の場合もマッピングテーブルを用意して実現できます。なので、RDBMS感覚で複雑なリレーションを張ってしまうと…、はいお察しですね。

というわけであくまでもシンプルな構成、リレーション張るにしても感覚的には1階層くらいかなと思っています。繰り返しますが、餅は餅屋です。

まとめ

というわけでAWS Amplifyはドンピシャにはまるケースだととても簡単にフロントエンド側からバックエンドのプロビジョニングや設定も行えるのでそういう場合はおすすめです。

一方でこのようなEase of Developmentを実現する系のツールセットはあくまでもシンプルなものを手軽に素早く立ち上げるのには向いていますが、一歩そこからはみ出て凝ったことをしようと思うとそのメリットを享受できなくなることもしばしばあります。

特にAmplifyのようにビルディングブロックとして存在するバックエンドをWrapする形で実現しているものはその傾向が強いと思います(仕方がないとも思います)。

GraphQLの使い勝手と相まってプロトタイプ開発なんかだとほんといいと思うので時間があればぜひ試してみてください。あとはAWSリソースを直接触る場合も!

©Keisuke Nishitani, 2020   プライバシーポリシー