とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
各種SNSのフォローもよろしくお願いいたします。

Amplifyを使ってWebアプリを作ろう(書籍登録編)

こんにちは。 アドベントカレンダー21日目です。終盤に差し掛かってきました。 今年はフロントエンドまわりであれこれ開発することが多かったので、振り返りを兼ねてこれからフロントエンド開発を始める方向けに入門編としてお送りしています。 最終日の25日には何かアプリケーションができていることが目標です。

↓最初から読み始めたい方はこちらか↓

devops-blog.virtualtech.jp

おさらい

今回のはなし

第18回からAmplifyシリーズでお送りしています。前回のAPI機能を作成する際にテーマ性を持ってやろう、と決めて「書籍管理アプリ」を作ることにしました。今回は実際にAPI機能を使って書籍情報を登録する処理を作ってみましょう。

準備

これまでの記事であれこれ追加したものを1度綺麗にお掃除しました。以下のようなディレクトリ構成に変更しています。

$ tree ./pages/
./pages/
├── app.css
├── _app.tsx
├── book
│   └── resister.tsx
└── index.tsx

画面UIの作成

まずは登録フォームの画面から作成していきましょう。これまでに触れた内容ですのでコードの細かい解説は省略します。次のサンプルコードをpages/book/resister.tsxに貼り付けてください。

まだ具体的な処理を書いていませんが、登録ボタンを押すとコンソールログに送信内容が表示されるようにしています。

サンプルコードはこちら

"use client";

import { CreateBookInput } from "@/src/API";
import { useFieldArray, useForm } from "react-hook-form";

type AutherInputs = {
  name: CreateBookInput["name"];
}

type Inputs = {
  name: CreateBookInput["name"];
  authers: AutherInputs[];
  publisher: CreateBookInput["publisher"];
};

export default function BookResister() {
  const { register, control, handleSubmit, formState: {
    isValid,
    isDirty,
    isSubmitting,
  } } = useForm<Inputs>({
    mode: "onChange",
    defaultValues: {
      name: "",
      authers: [],
      publisher: "",
    },
  });

  const { fields, append, remove } = useFieldArray({
    control,
    name: "authers",
  });

  const onSubmit = (data: Inputs) => {
    console.log(data);
  }

  return (
    <div className="p-5 container mx-auto md">
      <h3 className="text-3xl font-bold dark:text-white">書籍登録</h3>
      <div>
        <div>
          <label
            htmlFor="book_name"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
          >
            書籍名
          </label>
          <input
            id="book_name"
            type="text"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
            placeholder="サンプルの本"
            {...register("name", { required: true })}
          />
        </div>
        <div>
          <label
            htmlFor="author"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
          >
            著者
          </label>
          {fields.map((field, index) => (
            <div
              key={index}
              className="flex"
            >
              <input
                id="author"
                type="text"
                className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
                placeholder="著者"
                {...register(`authers.${index}.name`, { required: true })}
              />
              <button
                type="button"
                className="text-red-500 hover:text-red-700 whitespace-nowrap"
                onClick={() => remove(index)}
              >
                削除
              </button>
            </div>
          ))}
          <button
            type="button"
            className="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded"
            onClick={() => append({ name: "" })}
          >
            著者を追加
          </button>

        </div>
        <div>
          <label
            htmlFor="publisher"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
          >
            出版社
          </label>
          <input
            id="publisher"
            type="text"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
            placeholder="出版社"
            {...register("publisher", { required: true })}
          />
        </div>
        <div>
          <button
            type="submit"
            disabled={!isValid || !isDirty || isSubmitting}
            className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg disabled:opacity-50"
            onClick={handleSubmit(onSubmit)}
          >
            登録
          </button>
        </div>
      </div>
    </div>
  )
}

コードを実行したときに表示される画面はこんな感じです。

登録処理の作成

画面UIの準備ができたら次は登録処理を実装してみましょう。前回Amplifyで作成したAPIと接続してデータ登録をします。

createBookDataという関数を1つ作ります。関数の中ではGraphQLを通じてDynamoDBにデータを登録する処理を記述しています。

import { Book, CreateBookInput, CreateBookMutation } from "@/src/API";
import { createBook } from "@/src/graphql/mutations";
import { GraphQLResult, generateClient } from "aws-amplify/api";

export default async function createBookData(input: CreateBookInput) {
    const client = generateClient();
    const response = await client.graphql({
        query: createBook,
        variables: { input },
    }) as GraphQLResult<CreateBookMutation>;

    if (response.errors) {
        throw new Error(response.errors[0].message);
    }

    if (!response.data?.createBook) {
        throw new Error("Book not created");
    }

    const book: Book = response.data?.createBook;
    return book;
}

メインの処理はこの部分です。amplify pushした後にAPIクライアント ライブラリが自動で生成されるため、以下のように呼び出すだけでデータを登録する処理を書くことができます。

    const client = generateClient();
    const response = await client.graphql({
        query: createBook,
        variables: { input },
    }) as GraphQLResult<CreateBookMutation>;

それ以降の処理に関しては、レスポンス内容を取り扱いしやすいようにあれこれ加工しているところになります。今回はそこまで重要な処理ではないため、省略します。

コードが書けたので、実際に実行してみました。

画面上で適当な情報を入力したら、登録ボタンを押します。今回は、登録が成功したら登録されたデータをレスポンスとして返却されるようにしています。登録データの中でID項目はリクエストで指定していないので、レスポンス時に設定されていたら、無事にデータが登録されてユニークなIDが発行されたことになります。

レスポンス内容を見てみると無事にIDが設定されているので、登録に成功したようですね。

今日はこの辺で以上です。

おわりに

前回バックエンドにAPIを追加したので、今回はAPIを通じてDynamoDBにデータを登録する処理を書いてみました。次回は、登録したデータを取得して一覧表示するところをやっていきます。