本記事は Fusic Advent Calendar 2020 の1日目の記事です。
今年もいよいよはじまりました!Fusic社員が「個性をかき集めて、驚きの角度から世の中をアップデートしつづける」記事を書いていきますので、クリスマスまでどうぞよろしくお願いします。
というわけで、1日目は「AWS Amplify」に関する記事を書こうと思います。
はじめに
個人的に今年は「Amplify元年」といっても過言ではない年でした。Amplify SNS Workshop との出会いをきっかけに、AmplifyとNext.jsを組み合わせてモダンかつスピーディな開発ができないか模索するようになりました。
Amplifyを触るうちにさまざまなTipsを蓄積できたので、1年分まとめた逆引きTips集として公開することにしました。
※普段使用しているJavaScriptフレームワークがReact/Next.jsなので、記載内容もReact寄りです。ご了承くださいm(_ _)m
目次
- Auth
- サインアップ時に電話番号を不要としたい
- API(GraphQL)
- API.graphql()の結果をTypeScriptでいい感じに型解決できない
- Authでログインしたユーザ限定でAPIを実行できるようにしたい
- 項目をソートして取得したい
- schema.graphqlを更新しても諸々反映されない
- createdAt、updatedAtをスキーマから消したい
- Hosting
- 開発、ステージング、本番のインフラを分けたい
- hostingしたNext.jsのSPAが「403 Access Denied」となる
- 先にCI/CDを構築して後からauthやapiといったカテゴリのリソースを追加するとフロントエンドでエラーが発生する
- Function
- ランタイムにPythonを指定するとCI/CDがコケる
- 定期的にタスクを実行したい
- その他
- Amplifyの基本的な使い方をマスターしたい
- Categoryとして提供されていないAWSリソースを追加したい
- team-provider-info.jsonって何者?
- 上記で解決できない・よくわからない問題が起こっている
- まとめ
Auth
サインアップ時に電話番号を不要としたい
Amplify UI Componentsで作ったサインアップフォームは入力項目が多くて不便です。
コンポーネントに対してオプション指定することで、電話番号などの入力項目を減らせるようになっています。
注意点として、Reactに対応したAmplify UI Componentsは2つ存在しています。
- aws-amplify-react
- @aws-amplify/ui-react
どちらを使うかによって事情が変わってくるため、それぞれ解説します。
aws-amplify-reactの場合
aws-amplify-react
はLegacyなnpmパッケージです。
こちらのパッケージではReactのHOCとして、 withAuthenticator
が提供されています。Reactコンポーネントをexportする際に、このHOCでラップすることでその画面を認証必須にすることが可能です。
export default withAuthenticator(App);
この引数でオプション指定することによって、サインアップフォームから電話番号を省略できます。
export default withAuthenticator(App, {
signUpConfig: {
hiddenDefaults: ['phone_number']
}
});
@aws-amplify/ui-reactの場合
@aws-amplify/ui-react
はLatestなnpmパッケージです。
Latestなので現在ではこちらを使用することが推奨されています。こちらのパッケージでも withAuthenticator
が提供されています。しかし、 aws-amplify-react
とは異なり signUpConfig
をオプション指定することができないようです。
このため、HOCではなく AmplifyAuthenticator
コンポーネントを使ってフォームをカスタマイズします。
<AmplifyAuthenticator usernameAlias="email">
<AmplifySignUp slot="sign-up" formFields={[ { type: "email" }, { type: "password" } ]} />
<AmplifySignIn slot="sign-in" />
<AmplifySignOut />
<Component {...pageProps} />
</AmplifyAuthenticator>
API(GraphQL)
API.graphql()の結果をTypeScriptでいい感じに型解決できない
Amplifyを使うと、バックエンドと通信するためのクライアント(GraphQLラッパー)を自動生成してくれるので便利です。もちろん、これはTypeScriptにも対応しています。
TypeScript初心者な私は、API.graphql()の戻り値が型推論されず、対処方法に悩みました、
import API, { graphqlOperation, GraphQLResult } from '@aws-amplify/api'
import { listDevices } from '../src/graphql/queries'
const asyncFunc = async () => {
// これだと型推論が効かずエラーとなる
const result = await API.graphql(graphqlOperation(listDevices))
// 以降の処理...
}
いろいろ模索した結果、どうやら as
を使って型を明示するのが正解のようです。
import API, { graphqlOperation, GraphQLResult } from '@aws-amplify/api'
import { listDevices } from '../src/graphql/queries'
import { ListDevicesQuery } from '../src/API'
const asyncFunc = async () => {
const result = (await API.graphql(graphqlOperation(listDevices))) as GraphQLResult<ListDevicesQuery>
// 以降の処理...
}
Authでログインしたユーザ限定でAPIを実行できるようにしたい
GraphQLスキーマにて @auth
ディレクティブを指定することで、リクエスト時の権限チェックが可能です。
type Device @model
@auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
logs: [Log] @connection(keyName: "byDevice", fields: ["id"])
}
type Log @model
@auth(rules: [{ allow: owner }])
@key(name: "byDevice", fields: ["deviceId", "timestamp"]) {
id: ID!
deviceId: ID!
timestamp: AWSTimestamp!
value: Float!
device: Device @connection(fields: ["deviceId"])
}
上記では、allow: owner
としていますが、所有者以外にも権限指定したり、readやcreate権限を部分的に付与することも可能です。詳しくはドキュメントを参照ください。
https://docs.amplify.aws/cli/graphql-transformer/auth
項目をソートして取得したい
Amplify SNS Workshopで解説されている通り、 @key
ディレクティブを使ってDynamoDBにGSIを追加します。
type Post
@model (subscriptions: { level: public })
# パーティションキーをtype, ソートキーをtimestampに指定
# typeには常に 'post' を格納することで、全Postをtimestampでソートして取得する
@key(name: "SortByTimestamp", fields:["type", "timestamp"], queryField: "listPostsSortedByTimestamp")
{
type: String!
id: ID
content: String!
owner: String
timestamp: AWSTimestamp!
}
こうしておくと、次のコードのようにソート指定をしたFetchが可能となります。
const res = await API.graphql(graphqlOperation(listPostsSortedByTimestamp, {
type: "post", // パーティションキーとして 'post'(固定) を指定
sortDirection: 'DESC' // 降順指定
}));
schema.graphqlを更新しても諸々反映されない
amplify add api
を実行した時に、プロジェクト直下に schema.graphql
を作成するケースが多いのではないでしょうか?
後々になって、テーブルを追加する必要が出てきたりディレクティブを追記したりする場合に、このファイルを更新して amplify update api
しても反映されません。
なぜなら、 amplify/backend/api/{リソース名}/schema.graphql
が実際に構築されたGraphQL APIのスキーマとして読み込まれているためです。更新するときはこちらを更新して、 amplify update api
するようにしましょう。
createdAt、updatedAtをスキーマから消したい
DynamoDBにAppSync以外のリソースから値を書き込む場合など、createdAt
と updatedAt
が邪魔になるシチュエーションがあります。
そんなときは @model(timestamps: null)
を指定することで、createdAt
と updatedAt
を省略できます。
type Program
@model(timestamps: null) # createdAtとupdatedAtを削除
@key(name: "byStation", fields: ["stationId", "startedAt"]) {
id: ID!
name: String!
startedAt: AWSTimestamp!
endAt: AWSTimestamp!
stationId: ID!
station: Station @connection(fields: ["stationId"])
}
Hosting
開発、ステージング、本番のインフラを分けたい
Amplifyで作成したプロジェクトは環境を複数作成して、切り替えることが可能です。環境は amplify env add
することで追加できます。
Amplify SNS Workshop で構築手順が詳細に記載されていますので、こちらを参照ください。
hostingしたNext.jsのSPAが「403 Access Denied」となる
こちらの記事で解説しています。
AWS AmplifyでhostingしたNext.jsのSPAが「403 Access Denied」となったときの対処法
先にCI/CDを構築して後からauthやapiといったカテゴリのリソースを追加するとフロントエンドでエラーが発生する
CI/CD構築後にauthやapiといったバックエンドのリソースを追加すると、CI/CDでバックエンドリソースが構築されず、デプロイしたフロントエンド側でエラーが発生します。
推測ですが、Amplify hostingのCI/CDのビルド設定(amplify.yml
)は設定時点で、バックエンドのリソースが存在するかどうかを自動判定して、記述を変更しているようです。先にCI/CDを設定すると、バックエンドリソースは不要と判断して、ビルドが省略されます。
Next.js+TypeScript+AWS Amplify+RecoilでToDoリストを作るに記載したとおり、amplify.yml
を本来の形に書き換えることで、エラーを回避できます。
Function
ランタイムにPythonを指定するとCI/CDがコケる
Amplify側で指定したPythonのバージョンと、ビルド時に使用しているAmazon Linux2のPythonバージョンが異なることが原因です。
AWS AmplifyでPythonのFunctionをCI/CDするとbuildに失敗する問題の対処方法に記載したとおり、amplify.yml
を書き換えてPythonを再インストールすることで回避できます。
定期的にタスクを実行したい
amplify add function
する時に質問される Do you want to invoke this function on a recurring schedule?
という質問に Yes
と答えることで、LambdaのトリガーにCloudWatchEventが指定されます。
$ amplify add function
Scanning for plugins...
Plugin scan successful
? Select which capability you want to add: Lambda function (serverless function)
? Provide a friendly name for your resource to be used as a label for this category in the project: test
? Provide the AWS Lambda function name: test
? Choose the runtime that you want to use: Python
Only one template found - using Hello World by default.
? Do you want to access other resources in this project from your Lambda function? No
? Do you want to invoke this function on a recurring schedule? Yes
? At which interval should the function be invoked: Minutes
? Enter the rate in minutes: 3
? Do you want to configure Lambda layers for this function? No
? Do you want to edit the local lambda function now? No
Successfully added resource test locally.
その他
Amplifyの基本的な使い方をマスターしたい
はじめてAWS Amplifyを使うのであれば、Amplify SNS Workshopを1周することをお勧めします。
一方、このWorkshopだとカバーできていない部分があったり、使用しているパッケージのバージョンが一部古かったりという問題もあるので、他のチュートリアルもお勧めします。watilde/awesome-aws-amplify-ja にて日本語で書かれたチュートリアル記事がまとめられているので参考にしてみてください。
Categoryとして提供されていないAWSリソースを追加したい
Amplifyとして提供しているカテゴリ(authやapi)だけでは、構築したいシステムの要件を満たせないことがあります。
そんなときには Custom CloudFormation stacks を定義することで、AWSのリソースを自由にAmplifyのプロジェクトに組み込むことができます。もちろん、他のカテゴリからパラメータを渡すこともできます。例えば次のスライドに書いているような、apiで作ったDynamoDBのテーブル名をCustom CloudFormation stacksに渡してDynamoDBに値を書き込むIoT CoreのACTを定義する、といった使い方ができます。
https://speakerdeck.com/yuuu/aws-amplifytomockmockdeiotbatukuendowosupideinigou-zhu-suru?slide=25
team-provider-info.jsonって何者?
team-provider-info.json
にはAmplifyのプロジェクトで構築したリソースのARNなどが記載されています。IAMのSecret Access Keyなどが記載されているわけではないので、直接的にセキュリティリスクがある訳ではありませんが、AccountIdが記載されていることを踏まえるとあまりpublicにすべきではありません。
次のスライドにも記載されている通り、プライベートリポジトリの場合以外は.gitignoreに追加する
ようにしましょう。
上記で解決できない・よくわからない問題が起こっている
残念ながら、Amplifyを使っていると上記に挙がっていない問題にぶつかることはあると思われます。自分自身は以下のような方法で対処するようにしています。
エラーメッセージを理解する
Amplifyに限った話ではないですが、Amplifyのエラーメッセージには問題の原因や対処方法が記載されています。まずはこれを読んで理解します。
内容があまり理解できなかったとしても「Amplify + エラーメッセージから抜き出したいくつかのキーワード」で検索すると、似たような現象がGitHub Issuesで見つかるケースが多いです。
Amplify CLI/Frameworkのアップグレードをする
Amplifyは開発が活発で、CLI/Frameworkが頻繁にバージョンアップされます。公式ドキュメントに書かれているような基本的な操作が上手くいかない場合は、CLI/Frameworkをバージョンアップすることで問題を解決できるかもしれません。
AmplifyのGitHub Issuesを検索する
先にも挙げた通り、AmplifyのGitHub Issuesにはさまざまな事例が挙がっています。大抵の問題はIssuesを読んで解決するケースが多いのでぜひ参照してください。
まとめ
記事を書いて、AWS Amplifyはバックエンドを気にせずフロントエンド開発に注力できる環境を提供してくれる嬉しいサービスだなと改めて思いました。加えて、バックエンド周りも細かいカスタマイズに対応できるようになっている点が好印象です。
2021年は弊社の業務でのAmplifyの導入事例を、もっと世の中に発信できるよう精進します。