#はじめに
30代未経験からエンジニア転職をめざすコーディング初学者のYNと申します。お読みいただきありがとうございます。
今回ご紹介するAmplify
では、AWSのサービスを組み合わせて、驚くほど簡単に、サーバレスなwebアプリをデプロイし、かつCI/CDパイプラインまで作ることができます。
本記事では、チュートリアルに従って進めることで、Amplify
の基本的な仕組みの理解を備忘録としてまとめさせていただきました。
また、チュートリアルの通りに進めると、(2020年8月現在)ビルドエラーによりデプロイが失敗しますので、対処法も併せてまとめておきたいと思います。
#今回やったこと
チュートリアルに従い、Amplify
を使って、下記AWSサービスを組み合わせてReactアプリをデプロイしました。
DynamoDB
Cognito
AppSync
S3
CloudFront
#1. Reactアプリをデプロイし、パイプラインを作成
チュートリアル#1に従い、githubレポジトリにcommit(masterブランチにマージ)すると自動的にデプロイされるパイプラインを作ります。
Amplify
では、CloudFront
というAWSのCDNサービスと自動的に連携して、サーバレスにwebサイトをデプロイできます。
#2. Amplifyの初期設定を行う
チュートリアル#2をに従い、Amplify
の初期設定を行います。
やることは下記3つです。
- AmplifyCLIをローカルPCにグローバルインストール
$ npm install -g @aws-amplify/cli
$ amplify configure
- AWSバックエンドのセットアップ
$ amplify init --appId [your-app-id]
#3. Cognitoを使ってwebページにログイン機能を実装する
チュートリアル#3をに従って進めていきます。
Amplify
を使ってReactアプリをCognito
と連携させ、驚くほど簡単にユーザー管理機能を実装することができます。
やることは下記3つです。
- Amplifyライブラリのインストール(ルートディレクトリでnode_modulesに追加)
$ npm install aws-amplify @aws-amplify/ui-react
- Amplifyを使って認証機能の追加
$ amplify add auth
$ amplify push --y
上記の操作により、バックエンド設定ファイルが生成され、/src
にaws-exports.js
が追加されます。
- Reactに認証機能コンポーネントを追加
aws-exports.js
をフロントエンドで読み込むことにより、AmplifyのバックエンドとReactアプリをつなぐことができます。
// 下記をimport
import Amplify from 'aws-amplify';
import config from './aws-exports';
Amplify.configure(config);
import React from 'react';
import logo from './logo.svg';
import './App.css';
// 下記lコンポーネントをinportしてwrapする
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'
function App() {
return (
<div className="App">
<header>
<img src={logo} className="App-logo" alt="logo" />
<h1>We now have Auth!</h1>
</header>
<AmplifySignOut />
</div>
);
}
export default withAuthenticator(App);
ここで実装したユーザー管理情報は、Cognito
のコンソール画面で操作することができます。
この後、リモートのmasterブランチにマージすると自動的に変更が反映されてwebページがデプロイされます。
が、この状態ではビルドエラーが発生しますので、次に対処法をまとめます。
#3-2. ビルドエラーを解決する
チュートリアルの通りに進めるとビルドエラーによりデプロイが失敗しますので、ここに対処法をまとめます。
console画面より、ビルドに関して下記3点の追加設定が必要です。
- フロントエンドでバックエンドをデプロイする
下記のようにビルドのYML設定を追記します。
上記により、フロントエンドを構築するまえに適切なバックエンド環境を構築し、内部でaws-exports.js
を生成します。これをしないと、フロントエンドのビルド時にaws-exports.js
が見つからずエラーになってしまいます。
- フロントエンドでバックエンドリソースをデプロイするためのアクセス許可
上記のように、フロントエンドでバックエンドをデプロイするためには、ユーザーガイドに従い、サービスロールを作成する必要があります。
- ビルド設定を
Amplify CLI
の最新版に更新
ビルド設定のBuild image settings
を編集し、Amplify CLI
のversionを最新のものに更新します。これをしないと、デフォルトのruntimeに設定されているnodeのversionが古いため、ビルドが失敗します。
#4. GraphQL-APIを使ってReactアプリとデータベースを連携させる
チュートリアル#4をに従い、GraphQL
の初期設定、およびデータベースとの連携を行います。
*GraohQL
はfacebookが開発したwebAPIで、クエリ定義の自由度が高く、一度のリクエストで効率的に多くのデータを得ることができます。stateとの連携が容易でReactと相性がよく、とても使いやすいAPIです。
やることは下記3つです。
- GraphQL-APIとデータベースの設定
$ amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: notesapp
? Choose the default authorization type for the API: API Key
? Enter a description for the API key: demo
? After how many days from now the API key should expire: 7 (or your preferred expiration)
? Do you want to configure advanced settings for the GraphQL API: No, I am done.
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields
? Do you want to edit the schema now? Yes
- GraphQL-APIを
App-Sync
にデプロイ
$ amplify push --y
上記の操作により、AWSのApp-Sync
とDynamo-DB
のバックエンド自動的に構築されます。また、クエリーやミューテーションの設定ファイルが自動で生成されて/src/graphql
に追加されます。(すごい)
- GraphQLをつかってReactアプリとデータベースを連携させる
import React, { useState, useEffect } from "react";
import "./App.css";
import { API } from "aws-amplify";
import { withAuthenticator, AmplifySignOut } from "@aws-amplify/ui-react";
import { listNotes } from "./graphql/queries";
import {
createNote as createNoteMutation,
deleteNote as deleteNoteMutation,
} from "./graphql/mutations";
const initialFormState = { name: "", description: "" };
function App() {
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>
<AmplifySignOut />
</div>
);
}
export default withAuthenticator(App);
#5. S3を使ってReactアプリに写真を保存する
チュートリアル#5をに従い、ReactアプリとS3
を連携させて、写真をストレージに保存できるようにします。
やることは下記3つです。
- GraphQLスキーマの更新
type Note @model {
id: ID!
name: String!
description: String
image: String
}
- S3によるストレージ機能の追加
$ amplify add storage
$ amplify push --y
- ReactアプリとS3を連携させる
import React, { useState, useEffect } from "react";
import "./App.css";
import { API, Storage } from "aws-amplify";
import { withAuthenticator, AmplifySignOut } from "@aws-amplify/ui-react";
import { listNotes } from "./graphql/queries";
import {
createNote as createNoteMutation,
deleteNote as deleteNoteMutation,
} from "./graphql/mutations";
const initialFormState = { name: "", description: "" };
function App() {
const [notes, setNotes] = useState([]);
const [formData, setFormData] = useState(initialFormState);
useEffect(() => {
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 } },
});
}
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();
}
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>
<AmplifySignOut />
</div>
);
}
export default withAuthenticator(App);
これで、Cognito
AppSync
S3
を組み合わせてReactアプリをデプロイすることができました。
#最後に
Amplify
を使うことで、モダンなサーバーレスアプリを簡単にデプロイすることができます。コーディング初学者には敷居が高く感じられるAWSですが、とても親近感を感じることができました。
チュートリアルには、「50分で終わる」と書かれていましたが、コーディング初心者でもその程度で出来ました。如何にAmplify
が素晴らしいサービスであるかが納得いただけるかと思います。(ただし、ビルドエラーの対処には3日かかりました。苦笑)
お読みいただきありがとうございました。
#参考にさせていただいた記事
-
ビルド失敗時のエラー対処
https://stackoverflow.com/questions/59708481/aws-amplify-deploy-failure-due-to-aws-exports
https://github.com/aws-amplify/amplify-cli/issues/3149
https://docs.aws.amazon.com/ja_jp/amplify/latest/userguide/amplify-console-ug.pdf
https://shimba.io/tatsuyoshi/article/01E13AH3HGHRR0CQC4BNC966MF
https://kahoo.blog/aws-amplify-setup-frontend-and-backend-deploy/ -
Amplify解説
https://egghead.io/courses/building-serverless-web-applications-with-react-aws-amplify