AWS Amplify Gen1 を一通り使ってみてのメモ
AWS Amplify Gen1 をガッチリ使ってみたのですが、ドキュメントにないハマりポイントなどがいくつもあったので、その辺りや気づいたことを書いていきます。記事にする前に忘れそうなので一旦メモ。
フロント側はこっちで記事にしてくれてます。
Amplify のNext14ではSSR対応が不完全
「Amplify が Next.js SSR をサポート」とか言ってるんだがよく読むと、、、
サポートされていない 機能
- エッジ API ルート(エッジミドルウェアはサポートされていません)
- オンデマンドインクリメンタル・スタティック・リジェネレーション (ISR)
- 国際化 (i18n) 自動ロケール検出
- Next.js ストリーミング
- 静的アセットと最適化されたイメージでミドルウェアを実行する
とかしれっと言ってるので注意。特にNext.js ストリーミングはつまりSSR Streamingに対応してないので、Server componentsでSuspense
が使えない。すごい悲しい、、、。GithubにIssue要望も上がっていることなので、そのうち対応してくれることを期待したい。現状はSuspenseしたければClient componentsに切り出してやりましょう、、、。下記の記事でいろいろ試してくれているので参考になりました。
Amplify Studioは有効にしても良いが基本的に使うな
Amplifyの設定関連でAmplify Studioをつい使いたくなるかもしませんが、基本的にあそこで設定できることは全てAmplify CLIでやれるのでStudioは使わずCLIで設定しましょう。ユーザーの削除くらいしかあそこでやれることはないと思ったほうがいいです。どっちみちGen2ではStudioは無くなってるみたいだしね。
実際はユーザーの削除もサービスとしてはフラグ管理で実際に消すことはないので、Studioつかってユーザー消すのは開発の認証周りをやってる時だけだったりはする。
開発フローのベストプラクティス
これがなかなか難しい。とりあえずはGithubにmainとdevelopを作って、amplifyにdevとprodを作ってそれぞれを関連付けしてますがGItでPR作る前にPRを作る対象になるamplify のenvに対してamplify pushを行ってバックエンドの更新が確実にできるか?は確認したほうがいいです。この辺は何が良いのかわからないので難しい、、、。Gen2になるとSandbox機能が提供されると言うことなんですが、あまりイメージ湧いてない。。。
Cognitoでユーザー情報を管理するとCognitoの上限に引っかかるかも知れない
なので、Userテーブルを別に切るわけですが、その場合にそのUserテーブルにいつユーザー情報を書き込むか?というと
この辺りの記事を参考に Post Confirmation
でLambda関数をトリガーして、Lambda関数で良いようにUserテーブルにユーザーの初期情報を渡しておくといいと思います。これやるとCognitoは事実上ユーザー認証にしか使わないことになります。Userテーブル作ってそこから必要なユーザー情報を引っ張るほうが後々楽だった、、、
ある日を境にGraphQL Schemeの @function でinput型が効かなくなった
どう言うことや?と言う気持ちになったのですが、
このへんにこれ
schema {
query: Query
mutation: Mutation
}
type Author {
authorName: String
Books: [Book]
}
input AuthorInput {
authorName: String
Books: [BookInput]
}
type Book {
bookName: String
Authors: [Author]
}
input BookInput {
bookName: String
Authors: [AuthorInput]
}
type Query {
getAuthor(authorName: String): Author
getBook(bookName: String): Book
}
type Mutation {
addAuthor(input: [BookInput]): Author
addBook(input: [AuthorInput]): Book
}
input型ですが、当然 @functionしてるLambdaリゾルバを使ったMutationにも使える、、と思うんですが、実際はダメでした。ダメでした、と言うか途中からダメになりました。なんでや。探りに探ったのですが、結局わからなかったので直接Mutationに引数を書くことにして回避。なんでや。
Client componentsでのauthMode: 'iam'でのAPI認証
authMode: 'iam'
だけ書けばいけると思いきや、
import config from '@/amplifyconfiguration.json'
import { Amplify } from 'aws-amplify'
Amplify.configure(config, { ssr: true })
を /src/app/layout.ts
でやらないとならない。この辺は
この辺りを参考に ConfigureAmplifyClientSide.ts
を作ってlayout.tsで読み込んでやれば良いと思うのだがClient componetsやServer ActionなどでIAMで認証ができない(つまりUnauthorized userの認証をIAMで許可してるのに、APIがコールできない)場合には疑うと良いと思う。
正直このシチュエーションは常にあるのに世の中のサンプルはAPIをunauthedなユーザーが叩く事を気にしてない内容の記事が多い。というかAWSのドキュメントにもないくらいなのですが、ブログひとつ作れねーじゃねーかそれじゃ。と言う気持ちになる。
AmplifyやLambdaのtimezoneはUTC
で、これ変更できないので地味に面倒、、、。サービスによってはJSTで表示はしてくれたりとかあるけど、集計関連が絡む時に地味に面倒なので計画的にやりましょう、、、。
ソーシャルログインの実装でSignIn/OutのRedirect URLを複数設定すると詰む
詰む、と言うか現状はCLIから複数設定できるにも関わらず正しくAmplify側では設定されない。Cognitoには設定が通るがdevとprodがある場合あまり意味をなさない。
実際にはAmplifyのフロントエンドのデプロイ時に環境変数を渡して、必要なredirectのURLを
config.oauth.redirectSignIn =
process.env.NEXT_PUBLIC_OAUTH_REDIRECT_SIGN_IN || 'http://localhost:3000/auth/'
config.oauth.redirectSignOut =
process.env.NEXT_PUBLIC_OAUTH_REDIRECT_SIGN_OUT || 'http://localhost:3000/auth/'
こんな感じで環境に合わせて設定する必要がある。Githubに要望は出てる(らしい)けど、どうやら現状は「仕様です」らしい、、、。
ちなみにこれ、authしてるClient componentsで書かないとうまくいかない。layout.tsxとかで一箇所書いとけばいいやん、と思ってやったけどダメでした。なんでや?
Googleを使ったソーシャルログインでドメイン認証したいのでカスタムドメインをcognitoに設定した場合のAmplify側の設定
上記と似てるのだけど、これも普通に設定するとcognitoドメインをoauthのリダイレクトのURLに指定してくる(Amplify側で)。これを自分で明示的にCLIから設定する方法はない(はず)なので、これも環境変数でどうにかする。この時の oauth.domain で指定するのはhttps://がつかないやつなので注意。
if (process.env.NEXT_PUBLIC_OAUTH_DOMAIN) {
config.oauth.domain = process.env.NEXT_PUBLIC_OAUTH_DOMAIN
}
カスタムドメインをCognitoに設定する方法はそれほど難しくないので割愛(DNSに指定したカスタムドメインをAレコードでエイリアスを有効にして他のレコードに紐づければよし)。
AWS Backupの設定でAWSが出してる情報は微妙に舌足らず
とりあえずここを読むより、
こっち読んだほうが実務的には良いと思った。特に、、、
ここの認証周りの設定は見落とさずにやる。
あとはスクリプトがAWS CDKが v1 なので v2 にする必要がある(しなくても動くけど)。で、package.jsonで指定するnode moduleもv2対応にする。
Amplify側のドキュメントにもv2へのmigrationについてのドキュメンントがある。
で、AWS BackupではAWSの提供してくれているサンプルでも以下のパッケージも使ってるので忘れずに更新する。
"@aws-amplify/cli-extensibility-helper": "^3.0.0",
Amplifyの開発中にAPIKeyを使って開発するのは罠
APIKeyは長くて365日しか使えないし、プロダクションでは結局Cognito User Poolで認証ユーザーをIAMで非認証ユーザーの認証を行うことになるので、初めからその設定でやってdefault Authにはそのどちらかを設定してAPI Keyとは初めから縁を切っておいた方がいい。マジで。だってAPIKey更新できないんだもん(知ってる限り)。
Amplify Studioで自動生成されるLambda関数のnodejsのversion 16のサポート終了の連絡が来たら
こんなメールが来る。
お客様の AWS アカウントに現在、Node.js 16 ランタイムを使用する AWS Lambda 関数が 1 つ以上あることが判明したため、ご連絡いたしております。
Lambda での Node.js 16 のサポートは 2024 年 6 月 12 日に終了します。これは、2023 年 9 月 11 日に Node.js 16 がサポート終了 (EOL) したことに続くものです [1]。
Lambda ランタイムサポートポリシー [2] で説明されているように、Lambda の言語ランタイムのサポートはいくつかの段階で終了します。2024 年 6 月 12 日以降、Lambda は Lambda 関数で使用される Node.js 16 ランタイムにセキュリティパッチやその他の更新を適用しなくなり、Node.js 16 を使用する関数はテクニカルサポートの対象ではなくなります。また、Node.js 16 は AWS コンソールで使用できなくなりますが、AWS CloudFormation、AWS CLI、AWS SAM、またはその他のツールを使用して、Node.js 16 を使用する関数を作成および更新することはできます。2025 年 2 月 28 日以降、Node.js 16 ランタイムを使用する新しい Lambda 関数を作成することはできなくなります。2025 年 3 月 31 日以降、Node.js 16 ランタイムを使用する既存の関数を更新することはできなくなります。
2024 年 6 月 12 日までに、既存の Node.js 16 の関数を利用可能な最新の Node.js ランタイムにアップグレードすることをお勧めします。Node.js 16 の関数のリストは、AWS ヘルスダッシュボードの [影響を受けるリソース](Affected resources) タブにあります。
サポートの終了は関数の実行には影響しません。関数は引き続き実行されます。ただし、これらはサポートされていないランタイムで実行されるため、AWS Lambda チームによるメンテナンスやパッチの適用は行われません。
で実際のLambda関数を見にいくと確かにnode16の関数がある、、、がこれAmplify関連の自動生成の関数だな、、、というわけで調べてみたら ↓
で、この中でリンクが貼られているドキュメントがこれ ↓
あったあった。
To provide a passwordless login experience from AWS Amplify Console to Amplify Studio, Studio creates 4 Cognito Lambda triggers associated with the above-mentioned User Pool, named:
amplify-login-create-auth-challenge-SHORT_CODE
amplify-login-custom-message-SHORT_CODE
amplify-login-define-auth-challenge-SHORT_CODE
amplify-login-verify-auth-challenge-SHORT_CODE
これらのコードが確かにありました。で、これ何かと思ったらAmplify Studioに関したものでした。引き続きAmplify Studioを使うならoff/onしてもいいんだけど正直Amplify Studioを使う機会がほとんどない、何なら使うとトラブルの元になるのでオフにして関数が削除されて解決、、、。
Amplify Gen2がまだPreview版で仕事で使うのはちょっとな、と思うのですが早くGen2使ってみたいですね。Gen2はCLI Codeに一本化されてるし。
amplify add authなりした時の登録確認メールの日本語化
cognitoに行けば書き換えられるんですが amplify update auth
するたびにデフォルトに戻ってしまう。で、以下のように書くと、、、
Specify an email verification subject: ああああ
Specify an email verification message: いいいい {####} <br />うううう
cognito側ではこんな感じになる。
で、あれ改行が反映されてないじゃん、ってインターフェース的にはつい思ってしまうのだが、このテキストエリアの文言についてはHTMLでカスタマイズできる(と小さく書いてある)ので、見た目ばともかくとして最終的に送りたい文言をHTMLの改行である<br />
を使ってまとめて書けば良い、ということになる。
ずっと「うまく反映されてないのでは、、?」と勘違いしていた。
Cognitoから送信するユーザー作成時の確認メールのSESの設定
Cognitoを手作業でSESを使えるようにする(というかその前に必要なメールアドレスをSESに設定する必要があるが)と、何かの拍子で amplify update auth
した時に設定がSESではなく開発時のCognitoがメールを送るものに戻ってしまう。で、これを固定する方法はないのか?と思ってたところ amplify override
なるものがあることを知る。
Cognitoのメールを送信をSESで行うには以下のようにする。
amplify override auth
この時にcognitoのuserpoolを選択しておく。
で、SESのメール設定は割愛するが使えるメール設定がすでにあるとして、上記のコマンドで作成された custom.ts
に以下のように記述する。
import {
AmplifyAuthCognitoStackTemplate,
AmplifyProjectInfo,
} from '@aws-amplify/cli-extensibility-helper'
export function override(
resources: AmplifyAuthCognitoStackTemplate,
amplifyProjectInfo: AmplifyProjectInfo,
) {
resources.userPool.emailConfiguration = {
emailSendingAccount: 'DEVELOPER', // SES使用を指定する場合はこれを設定
from: 'no-reply <no-reply@example.com>', // Fromの表示設定はOptionだけど個人的にはしておいた方がいいと思う。
sourceArn: 'arn:aws:ses:ap-northeast-1:xxxxxxxx:identity/no-reply@example.com', // SESで検証済みメールアドレスのarn
}
}
amplify override auth ses
あたりでググると情報が出てくるけど、こういうのが体系になってたり公式のドキュメントにまとまっていない(よね?)あたりがamplifyの使ってて辛いところではある。。。gen2になるとCLIではなくCDKでの設定になるので上記のような設定をリソースごとにしていくことになるので、まぁいくらか見通しは良くなるのかなぁ、、、と思ったりしてます。
あ、そうそう npm i @aws-amplify/cli-extensibility-helper
もしておきましょう。
ブログの記事一覧、みたいなやつの管理画面を作ってる時にcreatedAtでソートしたいけど、データを取得する時にはソートキーがないと出来ない。で、記事ごとにステータスがある場合に enum
が使える。
これを使えば、
enum EntryStatus {
SCHEDULED
OPEN
CLOSE
}
type Entry {
~ 省略 ~
entryStatus: EntryStatus
@index(name: "EntryStatus", sortKeyFields: ["createdAt"], queryField: "entriesByEntryStatus")
~ 省略 ~
}
みたいなことができるのでステータス毎にソートして並べるとかはできる。id(@primaryKey)にソートキーでcreatedAtをつければいいじゃん。と思ったりするだろうけど、GSIとしてなんらかの値で絞り込みをかけた結果を記録していないとならないのでそれは出来ない(できるならぜひ教えて欲しいが、idをソートする、となると結局それってscanと変わらんじゃん?ということだよなぁ)。上の例だと entriesByEntryStatus というQueryをenumで指定した条件で絞り込んでそれをcreatedAtでソートすることが出来る。この辺はdynamoDBの戻り値が日付順とかで返ってくることを保証していない(と思う)のでどうにもならないところだと思う。
まーあとは戻り値をフロントエンドでソートする、という方法になるけどその辺は表示の量感にもよるだろうと思う。個人的にはステータス毎にタブを切り替える、みたいなUIにすればその方がスッキリするかな、と思うので、それはそれで良いのではないかと思っているが「全部一覧で見たい」となったらAppSyncが作成するlistを使うしかないだろう(Amplify Gen1でやれる範囲では)。