Amplify Gen2に触れてみた

## はじめに

この記事では、Amazon Amplify Gen2 を使用して効率的にバックエンドを構築する方法を紹介します。

### トピック

以下3点を具体例を交えて解説します。
- Amplifyプロジェクトの作成
- データモデルの設計
- バックエンドとフロントエンドの統合

## 0. (事前準備)フロントエンドのプロジェクトの作成

React と TypeScript を組み合わせたフロントエンドプロジェクトを作成します。
ここでは Vite を使用します。

```bash
npm create vite@latest my-react-app --template react-ts
```

上記コマンドを実行すると、プロジェクト名やテンプレートに関する情報を入力するよう求められます。
- React を選択
- TypeScript を選択 (SWC を選んでも良い)

my-react-app の部分は任意のプロジェクト名に置き換えることが可能です。

### プロジェクト作成後
ローカルでアプリケーションを立ち上げてみましょう。
```bash
  cd my-react-app
  npm install
  npm run dev
```

以下の画面が表示されれば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343165/16717168-f1e2-34b5-8c33-5e3859c84fd2.png)

エラーが出た場合、Node.js のバージョンが低い可能性があります。
その場合は、以下のコマンドで Node.js をアップデートしてください。
```bash:nvmを用いたアップデート例
nvm install --lts
nvm use v22.12.0
```

### 注意
初めてローカル環境でAmplifyを利用する場合、アカウント設定が必要です。
[こちらのページ](https://docs.amplify.aws/react/start/account-setup/)を参考に設定してみてください。
既にローカルで AWS アカウントとプロファイルを設定している場合は、対応不要です。

## 1. Amplifyプロジェクトの作成方法

ここからは、Amplify を使ったプロジェクトの作成手順を説明します。
[Amplify Documentation](https://docs.amplify.aws/)
　∟[Manuak installation](https://docs.amplify.aws/react/start/manual-installation/)を参考にAmplifyを導入してみましょう。

まず、以下のコマンドで Amplify プロジェクトを作成します。
```bash
npm create amplify@latest

? Where should we create your project? (.) # press enter
```

以下のようなディレクトリ構成が生成されます。
```bash
├── amplify/
│   ├── auth/
│   │   └── resource.ts
│   ├── data/
│   │   └── resource.ts
│   ├── backend.ts
│   ├── tsconfig.json
│   └── package.json
├── node_modules/
├── .gitignore
├── package-lock.json
├── package.json
└── tsconfig.json
```

次に、以下のコマンドを実行して必要なライブラリをインストールします。
```bash
npm add --save-dev @aws-amplify/backend@latest @aws-amplify/backend-cli@latest typescript
```

次にamplify/backend.tsを以下のように書き換えます。
```typescript:amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';

defineBackend({});
```

次にsand boxを起動します。
```bash
npx ampx sandbox
```

このコマンドを実行することで、
AWSアカウント上に、開発者ごとに独立したバックエンド開発環境を構築することができます。
便利。

AWSコンソール画面からSandboxを確認しましょう。
![スクリーンショット 2025-01-18 15.51.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343165/489ddea1-6f49-f29b-f7e0-05bcdc50ea1c.png)
↓
![スクリーンショット 2025-01-18 15.48.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343165/943b18ff-a9b3-ab1d-27fe-84c49b412bba.png)

サンドボックスが出来ていますね。

サンドボックスを起動した状態でローカル環境からバックエンドを更新すると、
その変更が自動的に検知され、クラウド側でもリアルタイムで更新が反映されます。
 Cognito の設定内容をローカルで調整すると、その内容が即座にクラウド側でも即座に反映されるため、開発とテストの効率が大幅に向上します。

Gen1ではamplify pushをコマンドを実行しバックエンドの更新を行う必要がありました。
便利。

## 2. データモデルの設計
Amplify を使用して、GraphQL ベースのデータモデルを定義します。
以下に、データモデルの例を示します。
[Set up Amplify Data](https://docs.amplify.aws/react/build-a-backend/data/set-up-data/)を見ると良いです。

### データモデルの例
```typescript:amplify/data/resource.ts

import { a, defineData, type ClientSchema } from '@aws-amplify/backend';

// スキーマの定義：データモデルを定義するためのメイン関数
const schema = a.schema({
  // a.model：フィールドを定義
  Todo: a.model({
      content: a.string(), // タスクの内容を表す文字列フィールド
    })
    .authorization(allow => [allow.publicApiKey()]) // 認可設定: 公開APIキーを許可
});

// スキーマ型をエクスポート
export type Schema = ClientSchema<typeof schema>;

// データリソースを定義
export const data = defineData({
  schema // 定義したスキーマを利用
});
```

[Set up Amplify Auth](https://docs.amplify.aws/react/build-a-backend/auth/set-up-auth/)
```typescript
// スキーマの定義
const schema = a.schema({
  Todo: a.model({
      content: a.string(), // タスクの内容を表す文字列フィールド
      isDone: a.boolean() // タスクの完了状態を示す真偽値フィールド
    })
    .authorization(allow => [allow.private()]) // 認可設定: 認証済みのCognitoユーザーのみアクセス可能
});
```

### 認可設定
authorization で、アクセス制御を設定しています。
例では誰でもアクセス可能な公開 API キーを許可していますが、以下のように認証済みのユーザーのみに制限することもできます。

| 認可方法               | アクセス対象                             | 主な用途              |
|------------------------|------------------------------------------|-----------------------|
| `allow.publicApiKey()` | 誰でもアクセス可能                       | 公共データの提供      |
| `allow.private()`      | 認証済みのCognitoユーザーのみアクセス可能 | ユーザー固有データの保護 |
| `allow.owner()`        | データ所有者のみアクセス可能             | 所有者ベースのアクセス制御 |
| `allow.groups()`       | 特定のグループ（例: 管理者、一般ユーザー）にアクセス制限 | 役割別のアクセス制御   |

なお、データモデルをカスタマイズしたい時は、[Customize your data model](https://docs.amplify.aws/react/build-a-backend/data/data-modeling/)を見ると良いと思います。

各フィールドの型を確認したい時は、[Add fields to data model](https://docs.amplify.aws/react/build-a-backend/data/data-modeling/add-fields/)を見ると良いと思います。

## 3. バックエンドとフロントエンドの統合
このセクションでは、Amplify の機能を実際に体験できる ToDo アプリの作成に取り組みます。
このチュートリアルを通じて、バックエンドとフロントエンドの統合手法を学び、リアルタイムでデータが更新されるアプリケーションの動作を確認してみましょう。

### 開発準備
ターミナルを 2 つ開いて、
さきほど作成したフロントエンドのプロジェクト(my-react-app)のディレクトリへ移動し、
以下のコマンドをそれぞれ実行してください。
バックエンドの変更をリアルタイムで確認しながらフロントエンドを動かすには、並行して起動する必要があります。

```bash
npx ampx sandbox
```

```bash
npm run dev
```

### いざ実装
以下コピペして該当ファイルを書き換えてください。
**コードの細かい書き方**は公式ドキュメントを確認してください。　

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

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

// Used for code completion / highlighting when making requests from frontend
export type Schema = ClientSchema<typeof schema>;

// defines the data resource to be deployed
export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'apiKey',
    apiKeyAuthorizationMode: { expiresInDays: 30 }
  }
});
```
:point_up:の補足

さっき紹介したデータモデルです。

---

```typescript:amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';
import { data } from './data/resource';

defineBackend({
  data,
});
```
:point_up:の補足
- defineBackend の役割
    - Amplifyバックエンド全体を構成する関数
    - data リソースを登録
- data リソースの内容
    - ./data/resource からインポート
    - データモデルやスキーマを含む事前定義済みリソース
- バックエンドリソースの構成
    - defineBackend の引数に渡されたオブジェクトでプロジェクト内のリソースを登録・構成
    - 必要に応じて認証やストレージリソースなどを追加可能

---

```typescript:App.tsx
import { useEffect, useState } from "react";
import type { Schema } from "../amplify/data/resource";
import { generateClient } from "aws-amplify/data";
const client = generateClient<Schema>();

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

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

    return () => sub.unsubscribe();
  }, []);

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

  return (
    <main>
      <h1>My todos</h1>
      <button onClick={createTodo}>+ new</button>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>{todo.content}</li>
        ))}
      </ul>
    </main>
  );
}

export default App;
```
:point_up:の補足

簡易的な「Todoアプリ」のフロント画面を作成しています。
Amplifyのバックエンドを活用し、データの取得、作成、表示を行います。

```typescript
const client = generateClient<Schema>();
```
Amplifyで定義したデータスキーマ（Schema）に基づくデータ操作クライアントを生成しています。

```typescript
const [todos, setTodos] = useState<Array<Schema["Todo"]["type"]>>([]);

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

  return () => sub.unsubscribe();
}, []);
```
Todo モデルのデータをリアルタイムで取得・監視しています。
nextコールバックでToDoモデルのデータが更新されるたびにsetTodosを呼び出して状態を更新します。
アプリを立ち上げて実際にタスク追加すると自動で画面に表示されているtodoが更新されますよね？
その仕組みを実現しています。

---

```typescript:main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';

Amplify.configure(outputs);

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>
)
```
:point_up:の補足

```typescript
import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';

Amplify.configure(outputs);
```
- amplify_outputs.json
    - Amplify CLIまたは設定時に生成されるファイルです
    - アプリケーションが使用するAWSバックエンドリソースの設定情報を含んでいます
- Amplify.configure()
    - AWS Amplifyを使用するために必要なリソース（認証、API、ストレージなど）の設定を読み込みます
    - 引数として渡されるoutputsには、amplify_outputs.json を指定します
つまりAmplifyの各機能を有効化しています。

---

sandboxの更新を待ちましょう。
```bash
[Sandbox] Watching for file changes...
File written: amplify_outputs.json
```
と表示されればOK

それでは早速、[http://localhost:5173/](http://localhost:5173/)に接続してアプリを確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343165/a78374d9-f360-49f2-02df-f84dd0bff4dc.png)

## まとめ
この記事では、Amazon Amplify Gen2 を使用したバックエンドの構築方法を以下3点に沿ってご紹介しました。
- Amplifyプロジェクトの作成
- データモデルの設計
- バックエンドとフロントエンドの統合

Amazon Amplify Gen2 の特長であるサンドボックス環境や型安全なデータモデル設計を活用することで、迅速かつ正確なアプリケーション開発が可能となります。
触り始めたばかりですので、ドキュメントを読み込み理解を深めていきたいと思います。