開発チームがお届けするブログリレーです!既に公開されている記事もありますので、こちらから他のメンバーの投稿もぜひチェックしてみてください!

はじめに

AWS Amplify Gen 2(以下Amplify Gen 2)は、フロントエンド開発者がフルスタックアプリケーションを構築するための新しい方法を提供しています。TypeScriptでデータモデル、認証、ストレージ、関数を記述するだけで、Amplifyが残りの部分を処理してくれます。今回は、Amplify Gen 2の初期状態からConnected formsで入力フォームを簡単に作成する方法について紹介します。

Connected formsとは?その驚くべきメリット

Connected formsは、Amplify Gen 2の強力な機能の一つで、アプリケーションのデータモデルと直接連携するフォームです。従来のフォーム実装と比較して以下のような圧倒的なメリットがあります。

  • 自動的なデータ連携: フォームが送信されると、入力データがそのまま指定したデータモデルに保存されます
  • コード記述量の大幅削減: 従来必要だったonSubmitハンドラーや状態管理のコードが不要になります
  • 型安全性: TypeScriptの型定義により、入力データとデータモデルの不整合を防止できます
  • 自動バリデーション: データモデルの制約に基づいて入力値を自動的に検証します
  • 双方向データバインディング: 更新フォームでは既存データを自動的に読み込み、表示します

Connected formsを使用すると、従来数時間かかっていたフォーム実装が数分で完了し、開発効率が飛躍的に向上します。

Connected formsの実装手順

データモデルの定義と展開

フォームを生成するには、データモデルが必要です。例として、Amplify Gen 2のテンプレートのTodoアプリを使用します。

まず、Amplify Gen 2のサンドボックス環境でデータモデルを定義し、デプロイします。

npx ampx sandbox

フォームの生成 – たった1コマンド

データモデルがデプロイされたら、以下のたった1つのコマンドを実行するだけでフォームが生成されます。

npx ampx generate forms

このコマンドは、スキーマで定義された各モデルに対して、作成フォームと更新フォームをui-componentsフォルダに生成します。従来のような複雑なフォーム構築作業はすべて自動化されます。

出力ファイル

File written: ui-components/graphql/subscriptions.ts
File written: ui-components/graphql/mutations.ts
File written: ui-components/graphql/queries.ts
File written: ui-components/TodoCreateForm.jsx
File written: ui-components/TodoCreateForm.d.ts
File written: ui-components/TodoUpdateForm.jsx
File written: ui-components/TodoUpdateForm.d.ts
File written: ui-components/utils.js
File written: ui-components/index.js

生成されるファイルの役割

生成されるファイルはそれぞれ以下の役割を持っています。

  • GraphQL関連ファイル
    • subscriptions.ts: リアルタイムでデータの変更を監視するためのGraphQLサブスクリプション定義
    • mutations.ts: データの作成・更新・削除をするためのGraphQLミューテーション定義
    • queries.ts: データを取得するためのGraphQLクエリ定義
  • フォームコンポーネント
    • TodoCreateForm.jsx: Todoを新規作成するためのReactフォームコンポーネント
    • TodoCreateForm.d.ts: 作成フォームのTypeScript型定義
    • TodoUpdateForm.jsx: 既存のTodoを更新するためのReactフォームコンポーネント
    • TodoUpdateForm.d.ts: 更新フォームのTypeScript型定義
  • ユーティリティ
    • utils.js: フォームの動作に必要なヘルパー関数群(バリデーション、データ変換など)
    • index.js: すべてのコンポーネントをエクスポートする中央ファイル

残念ながら、(jsxファイルには型定義ファイルも作成されていますが)TypeScriptではなくJavaScriptで生成される(jsxファイルには型定義ファイルも作成されています)ため、型安全性の恩恵を完全に受けられない点は改善の余地がありますが、これらのファイルにより、データモデルと連携した完全に機能するフォームが提供され、開発者は内部実装の詳細を気にすることなくすぐに使用できます。

アプリケーションへのフォームの統合

アプリケーションのエントリーポイントの設定

アプリケーションのエントリーポイントファイル(amplify/src/main.tsx)に以下のインポートと設定を追加します。

import '@aws-amplify/ui-react/styles.css';
import { ThemeProvider } from '@aws-amplify/ui-react';
import { Amplify } from 'aws-amplify';
import outputs from './amplify_outputs.json';

Amplify.configure(outputs);

さらに、コンポーネントをThemeProviderでラップします。



フォームの使用

生成されたフォームをインポートして使用します。以下はApp.tsxの例です。

import { useEffect, useState } from "react";
import type { Schema } from "../amplify/data/resource";
import { generateClient } from "aws-amplify/data";
import { TodoCreateForm, TodoUpdateForm } from "../ui-components";

const client = generateClient<Schema>();

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

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

  return (
    <main>
      <h1>My todos</h1>

      <h2>新しいTodoを作成</h2>
      <TodoCreateForm 
        onSuccess={() => {
          console.log("新しいTodoが作成されました");
        }}
      />

      <h2>Todoリスト</h2>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.content} 
            <button onClick={() => setSelectedTodo(todo)}>編集</button>
          </li>
        ))}
      </ul>

      {selectedTodo && (
        <div>
          <h2>Todoを編集</h2>
          <TodoUpdateForm
            id={selectedTodo.id}
            onSuccess={() => setSelectedTodo(null)}
          />
          <button onClick={() => setSelectedTodo(null)}>キャンセル</button>
        </div>
      )}
    </main>
  );
}

export default App;

Connected formsの使い方と特徴

フォームの種類

Connected formsには以下の2種類があります。

作成フォーム(Create Form)

作成フォームは空の入力欄を持つフォームをレンダリングします。データモデルに接続されている場合、送信時に常に新しいレコードが生成されます。バックエンドとの連携は完全に自動化されており、開発者が追加のコードを書く必要はありません。

更新フォーム(Update Form)

更新フォームは既存のデータを事前に入力して表示し、編集後に対象レコードを更新します。App.tsxのコードを見ると、更新フォームの実装は非常にシンプルです。

{selectedTodo && (
  <div>
    <h2>Todoを編集</h2>
    <TodoUpdateForm
      id={selectedTodo.id}
      onSuccess={() => setSelectedTodo(null)}
    />
    <button onClick={() => setSelectedTodo(null)}>キャンセル</button>
  </div>
)}

このコードから以下のポイントが分かります。

  1. idプロパティの使用
    • id={selectedTodo.id}と指定するだけで、自動的に対象レコードのデータが取得され、フォームに入力されます
    • データの取得ロジックを自分で書く必要はありません
  2. 成功時のコールバック
    • onSuccess={() => setSelectedTodo(null)}で更新成功時の動作を指定できます
    • この例では、更新が完了したら編集モードを終了(selectedTodoをnullに設定)しています
  3. 条件付きレンダリング
    • {selectedTodo && ( ... )}で、編集対象が選択されている場合のみフォームを表示
    • 編集用UIの表示・非表示の切り替えも簡単に実装できます

カスタマイズの自由度

Connected formsは高度に自動化されていますが、カスタマイズの自由度も高いのが特徴です。

  • スタイルのカスタマイズ
  • フィールドの追加・削除
  • バリデーションルールのカスタマイズ
  • 送信前後の処理の追加

App.tsxの例では、onSuccessコールバックを使用して、フォーム送信後の動作をカスタマイズしています。

<TodoCreateForm 
  onSuccess={() => {
    console.log("新しいTodoが作成されました");
  }}
/>

Connected formsは開発の生産性を維持しながら、ユーザー体験を最適化できます。

従来の実装との比較 – コード量の劇的な削減

Connected formsの価値をより理解するために、従来の実装と比較してみましょう。

従来の実装例

以下は、Connected formsを使用する前のAmplify Gen 2のテンプレートの初期状態の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(() => {
    client.models.Todo.observeQuery().subscribe({
      next: (data) => setTodos([...data.items]),
    });
  }, []);

  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>
      <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;

このコードでは単純なプロンプトでTodoを作成していますが、実際のアプリケーションでは、フォームの状態管理、バリデーション、エラーハンドリングなど多くのコードが必要になります。

従来の実装では、以下のような複雑な処理を自分で書く必要がありました。

  • 編集対象のデータをAPIから取得する処理
  • 取得したデータをフォームの初期値として設定
  • フォームの状態管理(編集中の値の保持)
  • バリデーション処理
  • 更新APIの呼び出し
  • エラーハンドリング
  • 成功時の状態更新

Connected formsの実装のポイント

Connected formsを使用することで、以下の点が大幅に改善されます。

  1. フォームの実装がシンプル
    <TodoCreateForm 
     onSuccess={() => {
       console.log("新しいTodoが作成されました");
     }}
    />
    
  2. データの読み込みと表示
    • Todo一覧の取得はclient.models.Todo.observeQuery()で実装
    • 削除はclient.models.Todo.delete()で実装
    • データの作成と更新はConnected formsが自動的に処理
  3. 削減されたコード量
    • 従来の実装では各フォームごとに状態管理、バリデーション、送信処理などの詳細なコードが必要
    • Connected formsを使用することで、これらのコードがすべて自動生成され、アプリケーションコードがシンプルになる

App.tsxのコード例からわかるように、Connected formsを使用することで、開発者は複雑なフォーム実装の詳細を気にすることなく、アプリケーションのビジネスロジックに集中できます。

まとめ – Connected formsで開発効率を劇的に向上

Amplify Gen 2のConnected formsを使用すると、数ステップでデータモデルに接続されたフォームを簡単に作成できます。Connected formsの利用で実現できる効果は以下の通りです。

  • 開発時間の大幅削減(数時間→数分)
  • コード量の削減(数百行→数行)
  • バグの可能性の低減
  • メンテナンスコストの削減

フロントエンドとバックエンドの連携を自動化することで、開発者はより創造的な作業に集中できるようになります。

データモデルを更新してフォームを再生成する場合は、元のui-componentsフォルダをバックアップしてからnpx ampx generate formsコマンドを実行してください。

Connected formsは、Amplify Gen 2が目指す「フロントエンド開発者がフルスタック開発を簡単に行える」というビジョンを最も体現した機能の一つです。ぜひ試してみてください!

リファレンス

Connected forms