こんにちは、福原です。
AWS Amplifyを使用してWEBアプリを作りたいと思い、以下のチュートリアルを実施してみました。
チュートリアルを進めていく中で詰まったところを記載しているので、参考にしていただけると嬉しいです。
初めにReactアプリを作成します。
以下のコマンドで実行します。
以前の記事で環境構築について記載しているので、Node.jsのインストールなど参考にしてください。
npx create-react-app todo |
Creating a new React app in /Users/xxxxxx/aws/react/todoapp. |
~省略~ |
Happy hacking! |
cd todo |
npm start |
Compiled successfully! |
You can now view todo in the browser. |
Local: http://localhost:3000 |
On Your Network: http://192.168.20.208:3000 |
Note that the development build is not optimized. |
To create a production build, use npm run build. |
webpack compiled successfully |
ブラウザが立ち上がり、以下画面が表示されるとOKです。
GitHubでリポジトリを作成します。
作成したReactアプリのGitを初期化します
初期化の前にReactアプリの.gitディレクトリを削除
※削除しないと、初期化できないため
ls -al | grep git |
drwxr-xr-x 12 xxxxxx staff 384 8 24 13:31 .git |
- rw-r--r-- 1 xxxxxx staff 310 8 24 13:31 .gitignore |
rm -rf .git/ |
作成したリポジトリにアプリケーションをプッシュします
git init |
Initialized empty Git repository in /Users/xxxxx/aws/react/todo/.git/ |
git remote add origin https://github.com/username/todo.git |
git remote -v |
origin https://github.com/username/todo.git (fetch) |
origin https://github.com/username/todo.git (push) |
git add . |
git commit -m "initial commit" |
git push origin master |
AWSにログインしAmplifyコンソールからReactアプリをデプロイします。
「ウェブアプリケーションをホスト」を選択
「GitHub」を選択し「続行」をクリック
GitHubの認証が成功すると、以下の画面が表示されます。
作成したリポジトリ/ブランチを選択し、「次へ」をクリック
※作成したリポジトリ/ブランチがない場合は、「View GitHub Permissions」を押して権限を追加します
リポジトリを選択し、「Save」をクリック
そのまま「次へ」をクリック
内容を確認し、「保存してデプロイ」をクリック
数分待つと、デプロイが完了しました
次にローカルでAmplifyアプリを初期化します。
まずAmplify CLIをインストールします
Amplify CLIとは各種AWSサービスを簡単に利用できる、コマンドベースのツールです。
npm install -g @aws-amplify/cli |
amplify configure |
Follow these steps to set up access to your AWS account: |
Sign in to your AWS administrator account: |
https://console.aws.amazon.com/ |
Press Enter to continue |
画面が表示されますが、コンソール通りにEnterをクリック
リージョン、名前を入力するとIAMユーザー作成画面が表示されます
Specify the AWS Region |
? region: ap-northeast-1 |
Specify the username of the new IAM user: |
? user name: xxxxxx |
Complete the user creation using the AWS console |
https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/users$new?step=final&accessKey&userNames=amplify-user&permissionType=policies&policies=arn:aws:iam::aws:policy%2FAdministratorAccess-Amplify |
Press Enter to continue |
ユーザー名を入力し、「次のステップ」をクリック
既存のポリシーが選択されているので、そのまま「次のステップ」をクリック
こちらもなにもせずに、「次のステップ」をクリック
内容を確認し、「ユーザーの作成」をクリック
認証情報(CSV)をダウンロードします
ターミナル(プロンプト)に戻り、Enter
先ほどの作成したアクセスID,アクセスキーを入力します
Enter the access key of the newly created user: |
? accessKeyId: ******************** |
? secretAccessKey: **************************************** |
This would update/create the AWS Profile in your local machine |
? Profile Name: default |
Successfully set up the new user. |
Amplify CLIの設定は完了したので、Amplify プロジェクトを初期化します
「-appId」は「アプリの設定」→「全般」→「アプリケーション ARN」の/までの末尾です
amplify init --appId xxxxx |
Note: It is recommended to run this command from the root of your app directory |
? Enter a name for the project todo |
The following configuration will be applied: |
Project information |
| Name: xxxxx |
| Environment: dev |
| Default editor: Visual Studio Code |
| App type: javascript |
| Javascript framework: react |
| Source Directory Path: src |
| Distribution Directory Path: build |
| Build Command: npm run-script build |
| Start Command: npm run-script start |
? Initialize the project with the above configuration? Yes |
Using default provider awscloudformation |
? Select the authentication method you want to use: AWS profile |
For more information on AWS Profiles, see: |
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html |
? Please choose the profile you want to use xxxxx |
Amplify AppID found: xxxx. Amplify App name is: xxxxx |
Adding backend environment dev to AWS Amplify app: xxxx |
⠇ Initializing project in the cloud... |
~省略~ |
Pro tip: |
Try "amplify add api" to create a backend API and then "amplify push" to deploy everything |
以下のAmplify ライブラリをインストールします
AWSサービスとやり取りするためのaws-amplify ライブラリ
固有のUIコンポーネントの@ aws-amplify/ui-react ライブラリ
npm install aws-amplify @aws-amplify/ui-react |
認証サービスをAmplifyプロジェクトに追加します。
amplify add auth |
Using service: Cognito, provided by: awscloudformation |
The current configured provider is Amazon Cognito. |
Do you want to use the default authentication and security configuration? Defau |
lt configuration |
Warning: you will not be able to edit these selections. |
How do you want users to be able to sign in? Username |
Do you want to configure advanced settings? No, I am done. |
✅ Successfully added auth resource todoca616e84 locally |
✅ Some next steps: |
"amplify push" will build all your local backend resources and provision it in the cloud |
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud |
「–y」オプションで全ての質問に「yes」を設定します。
amplify push --y |
~省略~ |
✔ All resources are updated in the cloud |
src/index.jsを開き、最後のインポートの下に次のコードを追加します
import Amplify from 'aws-amplify'; |
import config from './aws-exports'; |
Amplify.configure(config);- src/App.jsに認証フローを追加 |
App.jsを次のコードに更新します。
※チュートリアルのコードではライブラリのバージョンが使用できなかったため、以下のインポート文を追加しています
import '@aws-amplify/ui-react/styles.css'; |
import React from 'react'; |
import logo from './logo.svg'; |
import './App.css'; |
import { withAuthenticator } from '@aws-amplify/ui-react' |
import '@aws-amplify/ui-react/styles.css'; |
function App({ signOut, user }) { |
return ( |
<div className="App"> |
<header> |
<img src={logo} className="App-logo" alt="logo" /> |
<h1>Hello {user.username}</h1> |
<button onClick={signOut}>Sign out</button> |
</header> |
</div> |
); |
} |
export default withAuthenticator(App); |
以下の認証画面が表示されます。
GraphQL APIをアプリに追加します。
次のコマンドを実行します。
amplify add api |
? Select from one of the below mentioned services: GraphQL |
? Here is the GraphQL API that we will create. Select a setting to edit or conti |
nue Continue |
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, nam |
e, description) |
⚠️ WARNING: your GraphQL API currently allows public create, read, update, and delete access to all models via an API Key. To configure PRODUCTION-READY authorization rules, review: https://docs.amplify.aws/cli/graphql/authorization-rules |
✅ GraphQL schema compiled successfully. |
Edit your schema at /Users/fukuharatakuya/aws/react/todoapp/amplify/backend/api/todo/schema.graphql or place .graphql files in a directory at /Users/fukuharatakuya/aws/react/todoapp/amplify/backend/api/todo/schema |
✔ Do you want to edit the schema now? (Y/n) · yes |
Edit the file in your editor: /Users/fukuharatakuya/aws/react/todoapp/amplify/backend/api/todo/schema.graphql |
✅ Successfully added resource todo locally |
✅ Some next steps: |
"amplify push" will build all your local backend resources and provision it in the cloud |
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud |
Shema.graphqlファイルを以下に更新します。
type Note @model { |
id: ID! |
name: String! |
description: String |
} |
amplify push --y |
Src/App.jsを次のコードで更新します。
import React, { useState, useEffect } from 'react'; |
import logo from './logo.svg'; |
import './App.css'; |
import { withAuthenticator } from '@aws-amplify/ui-react' |
import '@aws-amplify/ui-react/styles.css'; |
import { API } from 'aws-amplify'; |
import { listNotes } from './graphql/queries'; |
import { createNote as createNoteMutation, deleteNote as deleteNoteMutation } from './graphql/mutations'; |
const initialFormState = { name: '', description: '' } |
function App({ signOut, user }) { |
const [notes, setNotes] = useState([]); |
const [formData, setFormData] = useState(initialFormState); |
useEffect(() => { |
fetchNotes(); |
}, []); |
async function fetchNotes() { |
const apiData = await API.graphql({ query: listNotes }); |
setNotes(apiData.data.listNotes.items); |
} |
async function createNote() { |
if (!formData.name || !formData.description) return; |
await API.graphql({ query: createNoteMutation, variables: { input: formData } }); |
setNotes([ ...notes, formData ]); |
setFormData(initialFormState); |
} |
async function deleteNote({ id }) { |
const newNotesArray = notes.filter(note => note.id !== id); |
setNotes(newNotesArray); |
await API.graphql({ query: deleteNoteMutation, variables: { input: { id } }}); |
} |
return ( |
<div className="App"> |
<h1>My Notes App</h1> |
<input |
onChange={e => setFormData({ ...formData, 'name': e.target.value})} |
placeholder="Note name" |
value={formData.name} |
/> |
<input |
onChange={e => setFormData({ ...formData, 'description': e.target.value})} |
placeholder="Note description" |
value={formData.description} |
/> |
<button onClick={createNote}>Create Note</button> |
<div style={{marginBottom: 30}}> |
{ |
notes.map(note => ( |
<div key={note.id || note.name}> |
<h2>{note.name}</h2> |
<p>{note.description}</p> |
<button onClick={() => deleteNote(note)}>Delete note</button> |
</div> |
)) |
} |
</div> |
<img src={logo} className="App-logo" alt="logo" /> |
<h1>Hello {user.username}</h1> |
<button onClick={signOut}>Sign out</button> |
</div> |
); |
} |
export default withAuthenticator(App); |
アプリを実行してみると、メモアプリが追加されました。
画像用のストレージを追加します。
amplify add storage |
? Select from one of the below mentioned services: Content (Images, audio, video, etc.) |
✔ Provide a friendly name for your resource that will be used to label this category in the project: · imageStorage |
✔ Provide bucket name: · test-amplify-image-storage-xxx |
✔ Who should have access: · Auth users only |
✔ What kind of access do you want for Authenticated users? · create/update, read, delete |
✔ Do you want to add a Lambda Trigger for your S3 Bucket? (y/N) · no |
✅ Successfully added resource imageStorage locally |
⚠️ If a user is part of a user pool group, run "amplify update storage" to enable IAM group policies for CRUD operations |
✅ Some next steps: |
"amplify push" builds all of your local backend resources and provisions them in the cloud |
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud |
画像をアップロードするために、Shema.graphqlファイルを以下に更新します。
type Note @model { |
id: ID! |
name: String! |
description: String |
image: String |
} |
amplify push --y |
Src/App.jsを次のコードで更新します。
import React, { useState, useEffect } from 'react'; |
import logo from './logo.svg'; |
import './App.css'; |
import { withAuthenticator } from '@aws-amplify/ui-react' |
import '@aws-amplify/ui-react/styles.css'; |
import { API, Storage } from 'aws-amplify'; |
import { listNotes } from './graphql/queries'; |
import { createNote as createNoteMutation, deleteNote as deleteNoteMutation } from './graphql/mutations'; |
const initialFormState = { name: '', description: '' } |
function App({ signOut, user }) { |
const [notes, setNotes] = useState([]); |
const [formData, setFormData] = useState(initialFormState); |
useEffect(() => { |
fetchNotes(); |
}, []); |
async function onChange(e) { |
if (!e.target.files[0]) return |
const file = e.target.files[0]; |
setFormData({ ...formData, image: file.name }); |
await Storage.put(file.name, file); |
fetchNotes(); |
} |
async function fetchNotes() { |
const apiData = await API.graphql({ query: listNotes }); |
const notesFromAPI = apiData.data.listNotes.items; |
await Promise.all(notesFromAPI.map(async note => { |
if (note.image) { |
const image = await Storage.get(note.image); |
note.image = image; |
} |
return note; |
})) |
setNotes(apiData.data.listNotes.items); |
} |
async function createNote() { |
if (!formData.name || !formData.description) return; |
await API.graphql({ query: createNoteMutation, variables: { input: formData } }); |
if (formData.image) { |
const image = await Storage.get(formData.image); |
formData.image = image; |
} |
setNotes([ ...notes, formData ]); |
setFormData(initialFormState); |
} |
async function deleteNote({ id }) { |
const newNotesArray = notes.filter(note => note.id !== id); |
setNotes(newNotesArray); |
await API.graphql({ query: deleteNoteMutation, variables: { input: { id } }}); |
} |
return ( |
<div className="App"> |
<h1>My Notes App</h1> |
<input |
onChange={e => setFormData({ ...formData, 'name': e.target.value})} |
placeholder="Note name" |
value={formData.name} |
/> |
<input |
onChange={e => setFormData({ ...formData, 'description': e.target.value})} |
placeholder="Note description" |
value={formData.description} |
/> |
<input |
type="file" |
onChange={onChange} |
/> |
<button onClick={createNote}>Create Note</button> |
<div style={{marginBottom: 30}}> |
{ |
notes.map(note => ( |
<div key={note.id || note.name}> |
<h2>{note.name}</h2> |
<p>{note.description}</p> |
<button onClick={() => deleteNote(note)}>Delete note</button> |
{ |
note.image && <img src={note.image} style={{width: 400}} /> |
} |
</div> |
)) |
} |
</div> |
<img src={logo} className="App-logo" alt="logo" /> |
<h1>Hello {user.username}</h1> |
<button onClick={signOut}>Sign out</button> |
</div> |
); |
} |
export default withAuthenticator(App); |
テストのメモと画像をアップロードできました。
バックエンドの部分や認証機能など簡単に設定できました。
今回はチュートリアルに沿って作成しましたが、
機能拡張や他のWEBアプリなどを作成できるようにAWSの勉強を続けていきたいと思います。