30
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

学んだこと

  • めちゃくちゃお手軽にアプリをデプロイできる
  • やはりQuickStartからまずやってみるのが一番いい
    • ハンズオンやってみた系で行こうとおもったらハンズオンの手順よりも新しくていい手順が途中で見つかって切り替えた
    • お忙しい方は ここで気づいた からみてください
  • 次回やってみたいこと

AWS Amplifyとは

AWS Amplify には、ウェブアプリケーションやモバイルアプリの構築に必要なものがすべて揃っています。開始もスケールも簡単です。

image.png

フルスタック React アプリケーションを構築する

AWS Amplify を使用してシンプルなウェブアプリケーションを作成する

image.png

今回はこの手順に沿ってやってみたいと思います。

1.Reactアプリケーションを作成

React アプリケーションを作成し、AWS Amplify のウェブホスティングサービスを利用してそれをクラウドにデプロイします

npx create-react-app amplifyapp
cd amplifyapp
npm start

image.png

2.Githubリポジトリを初期化

https://github.com/ でリポジトリを作成する

git init
git add .
git commit -m "initial commit"
git remote add origin git@github.com:username/reponame.git<作成したリポジトリURL>
git branch -M main
git push -u origin main

3.AWS Amplifyにデプロイする

https://console.aws.amazon.com/ から[AWS Amplify] を選択し、サービスコンソールを開きます。

image.png

アプリケーションをデプロイをクリックします。

image.png

Githubを選択して次へを押すと、Githubのアカウントの連携ページが開きます。

image.png

先ほど作成したリポジトリで許可をします。

保存してデプロイをクリックするとアプリのデプロイが実行されます。


4.コードの変更を自動的にデプロイする

コードを編集し、pushします。

src/App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h1>Hello from V2</h1>
      </header>
    </div>
  );
}

export default App;
git add .
git commit -m "changes for v2"
git push origin main

変更をpushしたらデプロイが走りはじめました。

変更を簡単に反映することができました。


ローカルAmplifyアプリを初期化する

Amplify CLI をインストール、設定します。

1.Amplify CLIをインストールする

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

Specify the AWS Region
? region:  us-east-1
Follow the instructions at
https://docs.amplify.aws/cli/start/install/#configure-the-amplify-cli

to complete the user creation in the AWS console
https://console.aws.amazon.com/iamv2/home#/users/create
Press Enter to continue

Enter the access key of the newly created user:

IAMユーザーを作成する。ポリシーはAdministratorAccess-Amplifyを指定する。

ここまでは問題なく進んだんですが、次のAmplifyアプリを初期化するのこのタブが見当たらず...

image.png

今はどういう感じでバックエンド構築するのか調べたところ、次のページが見つかりました。

Set up local AWS credentialsまではやってると思うので、Deploy cloud sandboxから実施します。

npx ampx sandbox
npm error could not determine executable to run

npm error A complete log of this run can be found in:

じ...実行できない

ちょっとUIから見てみます。
image.png

画面真ん中、mainのカード部分をクリックすると、次のような画面が開きます。

image.png

データのところを開いてみました。
これっぽいのでちょっとやってみます。

% npm create amplify@latest --legacy-peer-deps
Need to install the following packages:
create-amplify@1.0.6
Ok to proceed? (y) y


> amplifyapp@0.1.0 npx
> create-amplify

? Where should we create your project? .

(依存関係でinstallうまくいかなかったので --legacy-peer-depsつけました、のちに苦しみます)

% npx ampx sandbox
SSMCredentialsError: AccessDeniedException: User: arn:aws:iam:::user/amplify-dev is not authorized to perform: ssm:GetParameter on resource: arn:aws:ssm:us-east-1::parameter/cdk-bootstrap//version because no identity-based policy allows the ssm:GetParameter action
Resolution: Make sure your AWS credentials are set up correctly and have permissions to call SSM:GetParameter

なにやらアクセス権限のエラーが出てます。
作成したIAMユーザにAmazonSSMReadOnlyAccessポリシーを追加しました。
3分くらい待って反映されたので再度 npx ampx sandbox を実行...

image.png

今すぐ設定を初期化をクリック

できたっぽいです。

image.png

さっき無理くりバージョンエラー通したとこのせいでデプロイ通らない...
バージョンを修正します。

npm install "typescript@^4.0.0"
git add .
git commit -m “バージョン修正”
git push origin main

image.png

無事通りました:sweat:

認証を追加する

Amplify CLI とライブラリを使用して、認証を設定し、アプリに追加します

ハンズオンの手順だとGen1のCLI使ってるのでGen2の手順でやっていきます

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

npm add @aws-amplify/ui-react

2.フロントに認証フローを追加する

src/App.js
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import outputs from "amplify_outputs.json";
import { Amplify } from 'aws-amplify';

Amplify.configure(outputs);

export default function App() {
  return (
    <Authenticator>
      {({ signOut, user }) => (
        <main>
          <h1>Hello {user?.username}</h1>
          <button onClick={signOut}>Sign out</button>
        </main>
      )}
    </Authenticator>
  );
}

amplify_outputs.jsonが元々プロジェクトルートにあったんですがエラーになってしまうのでsrcに移しました

image.png

なんとか表示はできました

ここで気づいた

ハンズオンの手順いろいろ古いのでそれでいろいろつまづいている...?
下記のクイックスタートからやった方が楽だったかも...?
https://docs.amplify.aws/react/start/quickstart/

ちょっとやり直してみました。

Deploy a fullstack app to AWS

1. テンプレートからリポジトリを作成する

image.png

image.png

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

初回との違いみたいなとこで行くと... フレームワークの部分がAmplify Gen 2になっている :thinking:

image.png

image.png

image.png

保存してデプロイすると次のようなページが起動しました。

image.png

Make frontend updates

3. ローカル環境を準備する

git clone https://github.com/<github-user>/amplify-vite-react-template.git
cd amplify-vite-react-template && npm install

amplify_outputs.jsonをダウンロードし、プロジェクトルートに配置する。

image.png

├── amplify
├── src
├── amplify_outputs.json <== backend outputs file
├── package.json
└── tsconfig.json

4.TODO削除関数を実装する

リストがクリックされたらTodoが削除されるように変更します。

src/App.tsx
import { generateClient } from "aws-amplify/data";
import { useEffect, useState } from "react";
import type { Schema } from "../amplify/data/resource";

const client = generateClient<Schema>();

function App() {
  const [todos, setTodos] = useState<Array<Schema["Todo"]["type"]>>([]);

  useEffect(() => {
    client.models.Todo.observeQuery().subscribe({
      next: (data) => setTodos([...data.items]),
    });
  }, []);

  function createTodo() {
    client.models.Todo.create({ content: window.prompt("Todo content") });
  }

  function deleteTodo(id: string) {
    client.models.Todo.delete({ id })
  }

  return (
    <main>
      <h1>My todos</h1>
      <button onClick={createTodo}>+ new</button>
      <ul>
        {todos.map((todo) => (
          <li 
            onClick={() => deleteTodo(todo.id)}
            key={todo.id}>
            {todo.content}
          </li>
        ))}
      </ul>
      <div>
        🥳 App successfully hosted. Try creating a new todo.
        <br />
        <a href="https://docs.amplify.aws/react/start/quickstart/#make-frontend-updates">
          Review next step of this tutorial.
        </a>
      </div>
    </main>
  );
}

export default App;

サーバー起動

npm run dev

http://localhost:5173/ にアクセスして、TODOをクリックしたら削除できることを確認します。

5.ログインUIを実装する

src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { Authenticator } from '@aws-amplify/ui-react';
import App from "./App.tsx";
import "./index.css";
import { Amplify } from "aws-amplify";
import outputs from "../amplify_outputs.json";
import '@aws-amplify/ui-react/styles.css';

Amplify.configure(outputs);

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <Authenticator>
      <App />
    </Authenticator>
  </React.StrictMode>
);

src/App.tsx
import { useAuthenticator } from '@aws-amplify/ui-react';
import { generateClient } from "aws-amplify/data";
import { useEffect, useState } from "react";
import type { Schema } from "../amplify/data/resource";

const client = generateClient<Schema>();

function App() {
  const { signOut } = useAuthenticator();
  const [todos, setTodos] = useState<Array<Schema["Todo"]["type"]>>([]);

  useEffect(() => {
    client.models.Todo.observeQuery().subscribe({
      next: (data) => setTodos([...data.items]),
    });
  }, []);

  function createTodo() {
    client.models.Todo.create({ content: window.prompt("Todo content") });
  }

  function deleteTodo(id: string) {
    client.models.Todo.delete({ id })
  }

  return (
    <main>
      <h1>My todos</h1>
      <button onClick={createTodo}>+ new</button>
      <ul>
        {todos.map((todo) => (
          <li 
            onClick={() => deleteTodo(todo.id)}
            key={todo.id}>
            {todo.content}
          </li>
        ))}
      </ul>
      <div>
        🥳 App successfully hosted. Try creating a new todo.
        <br />
        <a href="https://docs.amplify.aws/react/start/quickstart/#make-frontend-updates">
          Review next step of this tutorial.
        </a>
      </div>
      <button onClick={signOut}>Sign out</button>
    </main>
  );
}

export default App;

メールアドレスとパスワードをいれてCreateAccountします。

image.png

メールに記載されてるConfirmation Codeを入力し、Confirmをクリックします。

image.png

SignOutをクリックしたり、登録したメールアドレスとパスワードでログインできることを確認しました。

6.変更を反映する

git commit -am "added authenticator"
git push

Make backend updates

ここまででもう データも認証も使えるようになっている... 最新の手順めっちゃいいですね

image.png

スクリーンショット 2024-11-29 21.35.08.png

npx ampx sandbox

本番ブランチに影響を与えずにバックエンドを更新するには、Amplify のクラウド サンドボックスを使用します。この機能は、チーム内の各開発者に個別のバックエンド環境を提供するため、ローカル開発とテストに最適です。

7. ユーザーごとの認証を実装する

amplify/data/resource.ts
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
    }).authorization(allow => [allow.owner()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
  },
});

現在は本番ではなく、sandboxを使用してるので本番で登録しているユーザではログインができないです。
新しくユーザを登録します。

スクリーンショット 2024-11-29 21.45.58.png
スクリーンショット 2024-11-29 21.48.30.png

sandboxで登録したTODOは本番に影響がないことも確認しました。
スクリーンショット 2024-11-29 21.49.39.png
image.png

変更を反映する

git commit -am "added per-user data isolation"
git push

8. 新しいデータを追加してみた

content、name、descriptionを持つNoteを追加します

data/resource.ts
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
    }).authorization(allow => [allow.owner()]),
  Note: a.model({
    content: a.string(),
    name: a.string(),
    description: a.string()
  }).authorization(allow => [allow.owner()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
  },
});
App.tsx
import { Button, Flex, Heading, Text, TextField, useAuthenticator, View } from '@aws-amplify/ui-react';
import { generateClient } from "aws-amplify/data";
import { ChangeEvent, FormEvent, useEffect, useState } from "react";
import type { Schema } from "../amplify/data/resource";

const client = generateClient<Schema>();

function App() {  
  const { user, signOut } = useAuthenticator();
  const [todos, setTodos] = useState<Array<Schema["Todo"]["type"]>>([]);
  const [notes, setNotes] = useState<Array<Schema["Note"]["type"]>>([]);
  const [formData, setFormData] = useState({
    name: '',content: '',description: ''
  });
  useEffect(() => {
    client.models.Todo.observeQuery().subscribe({
      next: (data) => setTodos([...data.items]),
    });
    client.models.Note.observeQuery().subscribe({
      next: (data) => setNotes([...data.items]),
    });
  }, []);

  async function createNote(e?: FormEvent<HTMLFormElement>) {
    e?.preventDefault();
    client.models.Note.create(formData);
    setFormData({
      name: '',content: '',description: ''
    });
  }

  function createTodo() {
    client.models.Todo.create({ content: window.prompt("Todo content") });
  }

  function deleteNote(id: string) {
    client.models.Note.delete({ id });
  }

  function deleteTodo(id: string) {
    client.models.Todo.delete({ id })
  }

  const changeNote = (e: ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setFormData({
      ...formData,
      [name]: value,
    });
  };
  return (
    <main>
      <h1>{user?.signInDetails?.loginId}'s todos</h1>
      <button onClick={createTodo}>+ new</button>
      <ul>
        {todos.map((todo) => (
          <li 
            onClick={() => deleteTodo(todo.id)}
            key={todo.id}>
            {todo.content}
          </li>
        ))}
      </ul>
      <h1>{user?.signInDetails?.loginId}'s notes</h1>
      <View as="form" margin="3rem 0" onSubmit={createNote}>
        <Flex direction="row" justifyContent="center">
          <TextField
            name="name"
            placeholder="Note Name"
            label="Note Name"
            labelHidden
            variation="quiet"
            onChange={changeNote}
            required
          />
          <TextField
            name="description"
            placeholder="Note Description"
            label="Note Description"
            labelHidden
            variation="quiet"
            onChange={changeNote}
            required
          />
          <TextField
            name="content"
            placeholder="Note Content"
            label="Note Content"
            labelHidden
            variation="quiet"
            onChange={changeNote}
            required
          />
          <Button type="submit" variation="primary">
            Create Note
          </Button>
        </Flex>
      </View>
      <Heading level={2}>Current Notes</Heading>
      <View margin="3rem 0">
        {notes.map((note) => (
          <Flex
            key={note.id || note.name}
            direction="row"
            justifyContent="center"
            alignItems="center"
          >
            <Text as="strong" fontWeight={700}>
              {note.name}
            </Text>
            <Text as="span">{note.content}</Text>
            <Button variation="link" onClick={() => deleteNote(note.id)}>
              Delete note
            </Button>
          </Flex>
        ))}
      </View>
      <div>
        🥳 App successfully hosted. Try creating a new todo.
        <br />
        <a href="https://docs.amplify.aws/react/start/quickstart/#make-frontend-updates">
          Review next step of this tutorial.
        </a>
      </div>
      <button onClick={signOut}>Sign out</button>
    </main>
  );
}

export default App;
30
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?