Closed7

【AWS公式チュートリアル】「Amazon Amplify でウェブアプリケーションをデプロイする」対応メモ

noknok

開発環境

MacPCにAWS CLIをインストールして進めます。

環境 バージョンなど 備考
MacOS Sonoma v14.5 -
AWS CLI aws-cli/2.17.22 Python/3.11.9 Darwin/23.5.0 exe/x86_64 -
Node.js v20.11.1 元々PCに入っていたバージョンを利用
npm v10.2.4 Node.jsと一緒にインストールされたバージョンを利用

※環境に関する補足
今まではCloud9上でコマンド実行などしていたのですが、2024年7月25日にCloud9の新規利用受付を終了した様です。
参考:AWS、「Cloud9」「CodeCommit」などの新規利用を終了 エバンジェリスト明かす

noknok

モジュール 1: 新しい Amplify プロジェクトを作成する

環境を確認する

Node.jsのバージョン確認

node -v
# v16.16.0

npm -v
# 8.11.0

Amplify CLIのインストール

npm install -g @aws-amplify/cli

Amplify CLIの設定

amplify configure

# 本来はブラウザ上でIMAユーザー作成したあと、リージョン情報など入力する
Specify the AWS Region
? region:  # Your preferred region
Specify the username of the new IAM user:
? user name:  # User name for Amplify IAM user
Complete the user creation using the AWS console

# 作成したIAMユーザーの認証情報を設定する
Enter the access key of the newly created user:
? accessKeyId:  # YOUR_ACCESS_KEY_ID
? secretAccessKey:  # YOUR_SECRET_ACCESS_KEY
This would update/create the AWS Profile in your local machine
? Profile Name:  # (default)

Successfully set up the new user.

React アプリケーションを作成する

Reactアプリケーション作成後、「amplify init」でAmplifyを初期化する

# note npx is installed with npm
npx create-react-app amplify-app
cd amplify-app
amplify init
# 実行内容詳細
% amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project amplifywebapp
The following configuration will be applied:

Project information
| Name: amplifywebapp
| 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 amplify-dev-profile
Adding backend environment dev to AWS Amplify app: xxxxxxxxxxxxx

Deployment completed.
noknok

モジュール 2: フロントエンドを構築し、API を接続する

Amplify ライブラリをインストールする

ライブラリのインストール

npm install @aws-amplify/ui-react

「src/App.js」のコード修正

-- import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
-- import Amplify from 'aws-amplify';
-- import awsExports from './aws-exports';
++ import React from 'react';
++ import { Amplify } from 'aws-amplify';

++ import { withAuthenticator, Authenticator } from '@aws-amplify/ui-react';
++ import '@aws-amplify/ui-react/styles.css';

++ import awsExports from './aws-exports';
Amplify.configure(awsExports);

function App() {
--   return (
--     <div>
--       <h1>Notes App</h1>
--       <AmplifySignOut />
--     </div>
--   );
++   return (
++     <Authenticator>
++       {({ signOut, user }) => (
++         <main>
++           <h1>Hello {user.username}</h1>
++           <button onClick={signOut}>Sign out</button>
++         </main>
++       )}
++     </Authenticator>
  );
}

export default withAuthenticator(App);

アプリケーションをテストする

  • ブラウザ起動後、ログイン画面が表示されたら成功
npm start

Amplify で GraphQL API を追加する

amplify add 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 continue Authorization modes: API key (default, expiration time: 7 days from now)
? Choose the default authorization type for the API Amazon Cognito User Pool
Using service: Cognito, provided by: awscloudformation
 
 The current configured provider is Amazon Cognito. 
 
 Do you want to use the default authentication and security configuration? Default 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 amplifywebappxxxxxxxx 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

? Configure additional auth types? No
? Here is the GraphQL API that we will create. Select a setting to edit or continue Conflict detection (required for DataStore): Disabled
? Enable conflict detection? No
? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)

GraphQL スキーマを作成する

type Note @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  text: String!
}

アプリケーションをデプロイする

amplify push
# 今回実行した内容の詳細
% amplify push
⠇ Building resource api/amplifywebapp⚠️ WARNING: owners may reassign ownership for the following model(s) and role(s): Note: [owner]. If this is not intentional, you may want to apply field-level authorization rules to these fields. To read more: https://docs.amplify.aws/cli/graphql/authorization-rules/#per-user--owner-based-data-access.
✅ GraphQL schema compiled successfully.

API を使用するようにフロントエンドを更新する

import { Component } from 'react';
-- import { API, graphqlOperation } from 'aws-amplify';
++ import { Amplify }  from 'aws-amplify';
++ import '@aws-amplify/ui-react/styles.css';
++ import { generateClient }  from 'aws-amplify/api';
import { createNote, deleteNote } from './graphql/mutations';
import { listNotes } from './graphql/queries';

-- import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
++ import { withAuthenticator, Authenticator } from '@aws-amplify/ui-react';
import awsExports from './aws-exports';

Amplify.configure(awsExports);
++ const API = generateClient();

class AddNote extends Component {
  constructor(props) {
    super(props);
    this.state = { text: '' };
  }

  handleChange = (event) => {
    this.setState({ text: event.target.value });
  }

  handleClick = () => {
    this.props.addNote(this.state);
    this.setState({ text: '' });
  }

  render() {
    return (
      <div style={styles.form}>
        <input
          value={this.state.text}
          onChange={this.handleChange}
          placeholder="New Note"
          style={styles.input}
        />
        <button onClick={this.handleClick} style={styles.addButton}>Add Note</button>
      </div>
    );
  }
}

class NotesList extends Component {
  render() {
    return (
      <div>
        {this.props.notes.map(note =>
          <div key={note.id} style={styles.note}>
            <p>{note.text}</p>
            <button onClick={() => { this.props.deleteNote(note) }} style={styles.deleteButton}>x</button>
          </div>
        )}
      </div>
    );
  }
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { notes: [] };
  }

  async componentDidMount() {
--     var result = await API.graphql(graphqlOperation(listNotes));
++     var result = await API.graphql({query: listNotes});
    this.setState({ notes: result.data.listNotes.items });
  }

  deleteNote = async (note) => {
    const id = {
      id: note.id
    };
--     await API.graphql(graphqlOperation(deleteNote, { input: id }));
++     await API.graphql({query: deleteNote, variables: {input: id}});
    this.setState({ notes: this.state.notes.filter(item => item.id !== note.id) });
  }

  addNote = async (note) => {
--     var result = await API.graphql(graphqlOperation(createNote, { input: note }));
++     var result = await API.graphql({query: createNote, variables: {input: note}});
    this.state.notes.push(result.data.createNote);
    this.setState({ notes: this.state.notes });
  }

  render() {
    return (
--       <div style={styles.container}>
--         <h1>Notes App</h1>
--         <AddNote addNote={this.addNote} />
--         <NotesList notes={this.state.notes} deleteNote={this.deleteNote} />
--         <AmplifySignOut />
--       </div>
++       <Authenticator>
++         {({ signOut, user }) => (
++           <div style={styles.container}>
++             <h1>Notes App</h1>
++             <AddNote addNote={this.addNote} />
++             <NotesList notes={this.state.notes} deleteNote={this.deleteNote} />
++             <button onClick={signOut}>Sign out</button>
++           </div>
++         )}
++       </Authenticator>
    );
  }
}

export default withAuthenticator(App);

const styles = {
  container: { width: 480, margin: '0 auto', padding: 20 },
  form: { display: 'flex', marginBottom: 15 },
  input: { flexGrow: 2, border: 'none', backgroundColor: '#ddd', padding: 12, fontSize: 18 },
  addButton: { backgroundColor: 'black', color: 'white', outline: 'none', padding: 12, fontSize: 18 },
  note: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: 22, marginBottom: 15 },
  deleteButton: { fontSize: 18, fontWeight: 'bold' }
}

動作イメージ

  • チュートリアルのUIと若干異なるが、動作したため良しとした
    ログイン画面
    ノート登録・削除画面
noknok

モジュール 3: デプロイを自動化する

継続的デプロイ

GitHubで新規リポジトリを作成し、ローカルのReactプロジェクトをGitHubと連携

git remote add origin https://github.com/xxxxx/amplify-xxxxx.git
git branch -M main
git push -u origin main

AWSマネジメントコンソールから、Amplify用のIAMロールを作成

  • IAMコンソールの「ロールの作成」より、以下内容で作成した(ロール名は適当な値を入力)
    • 信頼できるエンティティのタイプ: AWS のサービス
    • サービス: Amplify
    • ユースケース: Amplify - バックエンドデプロイ
    • アクセス許可ポリシー: AdministratorAccess-Amplify

Amplifyコンソール上の、「ソースコードプロバイダーを選択」から連携を実施

  • 画面に表示される項目を順次設定して実施した
    GitHub連携-1
    GitHub連携-2
    GitHub連携-3

ローカルのプロジェクトで「コミット&プッシュ」して、CI/CDの動作確認

  • ローカルのプロジェクトから「コミット&プッシュ」し、Amplifyのプロジェクトへデプロイ連携できたら完了
    ※今回はタイトル部分に少し変更を加えた後、「コミット&プッシュ」してCI/CDの動作を確認しました
    BeforeAfter
noknok

モジュール 4: リソースをクリーンアップする

AWS環境をクリーンアップする

バックエンド環境の削除

  • Amplifyコンソールから、以下を進めた
    対象のプロジェクト選択 > [アプリケーションの設定] > [全般設定] > [バックエンドを管理] > [アクション] > [削除] を選択

プロジェクトの削除

  • Amplifyコンソールから、以下を進めた
    対象のプロジェクト選択 > [アプリケーションの設定] > [全般設定] > [アプリの削除] を選択
noknok

チュートリアル全体を確認することができたため、クローズします。

このスクラップは3ヶ月前にクローズされました