🏎️

AWS Amplify Gen2とかいうの試してみた

2024/04/22に公開

team411の椙山です!
今回,AWSを散策していたら,Amplify Gen2なるものがあったので試してみました👍

AWS Amplify Gen2とは?

https://docs.amplify.aws/gen2/
公式サイトの概要のところをみてみましょう!

Amplify has reimagined the way frontend developers build fullstack applications on AWS. With this next generation of Amplify’s backend-building experience, you can author your frontend and backend definition completely with TypeScript, a file convention, and Git branch-based environments.

AWSは開発者のストレスを軽減して,開発者がより開発に集中しやすいこと,これをDeveloper Experience,DXとよびそのの改善に力をいれています!
https://aws.amazon.com/jp/blogs/news/the-future-of-web-development-aws-amplifys-code-first-approach/
あるあるのストレスが

  • APIとフロントの噛み合わせだけでなんでこんなに頭を使うの?
  • インフラの構築,設定のために慣れない言語を使わないとあかん...
    とかですね!これを減らしたい!!

ということで求められているのが,コードファーストアプローチです!
https://aws.amazon.com/jp/blogs/news/introducing-amplify-gen2/
開発者は眼の前のアイデアの実現に集中すればよいですし,これによってプロダクトを迅速に市場に投入することができます👍

実際にためしてみよう!

今回はこちらのチュートリアルにならって,触ってみます!
https://docs.amplify.aws/gen2/start/quickstart/nextjs-app-router-client-components/
このチュートリアルではおなじみToDoアプリを作成します!

  • ToDoアプリのフロントエンド構築
  • ToDoアプリのバックエンド構築
    ですね!

準備

今回はNext.js 13のApp Routerを使っていきます!

Next.jsプロジェクトの作成

npm create next-app@14 -- next-amplify-gen2 --typescript --eslint --app --no-src-dir --no-tailwind --import-alias '@/*'
cd next-amplify-gen2

AWS Amplifyの導入

npm create amplify@latest

あとはいつものAmplify同様,IAMユーザーにロールを振って,ログインすればOKです!
やり方は

amplify configure

を実行して,あとはシェルに従えばOKです.
詳しいやり方はこちらをどうぞ↓
https://docs.amplify.aws/react/start/getting-started/installation/

テスト

フロントエンドは例によって例のごとく,npm run devで試せます!
便利なのは,フロントエンドとバックエンドをいち早く統合するためのサンドボックス環境を用意することがワンラインで用意できることですね!
AWS CLIに登録したIAMユーザーのプロフィール名を指定してやってみましょう!

npx amplify sandbox --profile amplify-gen2

はじめて使う場合は設定の初期化を要求されるので実行しましょう!(数分で終わります👍)

これができたら完了ですね!

いざ開発

バックエンドを構築する

amplify/data/resource.tsをチェックしてみてください!
(わかりやすく翻訳しておきました👍)

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

/*== STEP 1 ===============================================================
フロントエンドのソースコードに移動します。
クライアント側のコードから、テーブルにCRUDLリクエストを行うデータクライアントを生成します。
(このスニペットは、フロントエンドのコードファイルでのみ機能します。)
JavaScriptまたはNext.jsのReactサーバーコンポーネント、ミドルウェア、サーバーアクション、またはPagesルーターを使用していますか?
これらのユースケースのためのデータクライアントの生成方法を確認してください:
https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
=========================================================================*/
const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
    })
    .authorization((allow) => [allow.guest()]),
});

export type Schema = ClientSchema<typeof schema>;

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

/*== STEP 2 ===============================================================
フロントエンドのソースコードに移動します。
クライアント側のコードから、テーブルにCRUDLリクエストを行うデータクライアントを生成します。
(このスニペットは、フロントエンドのコードファイルでのみ機能します。)
JavaScriptまたはNext.jsのReactサーバーコンポーネント、ミドルウェア、サーバーアクション、またはPagesルーターを使用していますか?
これらのユースケースのためのデータクライアントの生成方法を確認してください:
https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
=========================================================================*/

/*
"use client"
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";

const client = generateClient<Schema>() // use this Data client for CRUDL requests
*/

/*== STEP 3 ===============================================================
データベースからレコードを取得し、フロントエンドコンポーネントで使用します。
(このスニペットはフロントエンドのコードファイルでのみ動作します)
=========================================================================*/

/* For example, in a React component, you can use this snippet in your
  function's RETURN statement */
// const { data: todos } = await client.models.Todo.list()

// return <ul>{todos.map(todo => <li key={todo.id}>{todo.content}</li>)}</ul>

とありますね!
これ,何をいっているのかというと,この開発中を想定した私のVSCodeの画面をみたらわかりやすいです笑

左でフロントエンド,右でバックエンドを,言語はTypescriptだからまさにシームレス
フロントエンドを変更したらrun devでチェック.バックエンドもサンドボックス環境でチェック

シームレス,ストレスフリー,爆速開発が可能ですね笑
さっそくToDoのスキーマかいていきますか

amplify/data/resource.ts
...
const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
      done: a.boolean(),
      priority: a.enum(['low', 'medium', 'high']),
    })
    .authorization((allow) => [allow.guest()]),
});
...

これを変更してファイルをセーブすると,即座にサンドボックス環境にバックエンドが反映されます.
まじで快適ですね笑
APIキーのルールも変更しましょうか!

amplify/data/resource.ts
...
const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
      done: a.boolean(),
      priority: a.enum(['low', 'medium', 'high']),
    })
    .authorization((allow) => [allow.owner()]),
});
...

これでオーナーのみAPIキーを取得できます!
ユーザーの認証も設定しちゃいましょう!

amplify/data/resource.ts
...
export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
  },
});
...

たったこれだけの変更で,Amazon Cognitoのユーザープールを利用したサインインの設定が完了です!

ユーザー認証の追加

amplify/auth/resource.tsを確認してみてください!

amplify/auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';

/**
 * Define and configure your auth resource
 * @see https://docs.amplify.aws/gen2/build-a-backend/auth
 */
export const auth = defineAuth({
  loginWith: {
    email: true,
  },
});

ここにいろいろ認証の設定を書き込んでいきます!
たとえば,Eメール認証をして,メールの題名をWelcome! Verify your email!としたいなら

amplify/auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';

/**
 * Define and configure your auth resource
 * @see https://docs.amplify.aws/gen2/build-a-backend/auth
 */
export const auth = defineAuth({
  loginWith: {
    email: {
      verificationEmailSubject: 'Welcome! Verify your email!'
    },
  },
});

これでOKです!笑
aws/backend.tsをみると

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

/**
 * @see https://docs.amplify.aws/react/build-a-backend/ to add storage, functions, and more
 */
const backend = defineBackend({
  auth,
  data,
});

となっています!
はい.大規模な開発になったとしても,それぞれのスキーマ定義や設定ファイルをコンポーネント化して扱えば管理することも楽ちんです笑!

フロントエンドに認証機能を追加する!

AWSのUIライブラリを用意しましょう!

npm install @aws-amplify/ui-react

Amplifyのコンフィグファイルであるamplifyconfiguration.jsonを読み込むコンポーネントを作成します!

components/ConfigureAmplify.tsx
"use client";

import React from 'react';
import config from '@/amplifyconfiguration.json';
import { Amplify } from 'aws-amplify';

Amplify.configure(config, { ssr: true });

export default function ConfigureAmplifyClientSide() {
    return null;
}

これをlayout.tsx内で呼び出してあげれば,Amplifyの設定をクライアントサイドで読み込むことができます!

app/layout.tsx
import ConfigureAmplifyClientSide from "@/components/ConfigureAmplify";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <ConfigureAmplifyClientSide />
        {children}
      </body>
    </html>
  );
}

適当にpage.tsxを変更して...

app/page.tsx
"use client";

import { withAuthenticator } from "@aws-amplify/ui-react";
import "@aws-amplify/ui-react/styles.css";

function App() {
  return (
    <>
      <h1>Hello, Amplify 👋</h1>
    </>
  );
}

export default withAuthenticator(App);

はい!
ここで認証機能ができました笑!
ためしにlocalhost:3000をチェックしてみると...

Amplifyのログイン環境ができましたね!
ためしに使ってみますか!
新規アカウントを作成すると...

メールがしっかり送信されてますね!
Amazon Cognitoをチェックしてみると...

ちゃんとユーザー登録されていますね!
超簡単に実装できるわけですね〜!

ToDoリストの実装

まずはToDoリストです!

components/TodoList.tsx
// components/TodoList.tsx
"use client";

import { useState, useEffect } from "react";
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";

// generate your data client using the Schema from your backend
const client = generateClient<Schema>();

export default function TodoList() {
    const [todos, setTodos] = useState<Schema["Todo"][]>([]);

    async function listTodos() {
        // fetch all todos
        const { data } = await client.models.Todo.list();
        setTodos(data);
    }

    useEffect(() => {
        listTodos();
    }, []);

    return (
        <div>
            <h1>Todos</h1>
            <ul>
                {todos.map((todo) => (
                    <li key={todo.id}>{todo.content}</li>
                ))}
            </ul>
        </div>
    );
}

ポイントを解説!
amplify/data/resource.tsで定義したSchemaをインポートします!

import type { Schema } from "@/amplify/data/resource";

クライアントインスタンスをつくって

const client = generateClient<Schema>();

そしてここでStateとして扱います!

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

ミソは型で,バックエンドの場所で定義した型がここで利用できるので,このバックエンドとフロントエンドの結合における型安全性が保証されるわけですよ.
従来の開発において,いちいち型をtypeディレクトリに保存する,なんて手間は,もういりません笑!

最後にデータのフェッチの関数を定義します!

async function listTodos() {
    // fetch all todos
    const { data } = await client.models.Todo.list();
    setTodos(data);
}

扱いはまさしくコンポーネントですよ.楽ちん.

page.tsxTodoListをぶち込んで

"use client";

import TodoList from "@/components/TodoList";
import { withAuthenticator } from "@aws-amplify/ui-react";
import "@aws-amplify/ui-react/styles.css";

function App() {
  return (
    <>
      <h1>Hello, Amplify 👋</h1>
      <TodoList></TodoList>
    </>
  );
}

export default withAuthenticator(App);

はい,実際にみてみましょう!

まだデータは内のでこんな感じですね〜!

ToDo追加機能の実装

components/TodoList.tsx
// components/TodoList.tsx
"use client";

import { useState, useEffect } from "react";
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";

// generate your data client using the Schema from your backend
const client = generateClient<Schema>();

export default function TodoList() {
    const [todos, setTodos] = useState<Schema["Todo"][]>([]);

    async function listTodos() {
        // fetch all todos
        const { data } = await client.models.Todo.list();
        setTodos(data);
    }

    useEffect(() => {
        listTodos();
    }, []);

    return (
        <div>
            <h1>Todos</h1>
            <button onClick={async () => {
                // create a new Todo with the following attributes
                const { errors, data: newTodo } = await client.models.Todo.create({
                    // prompt the user to enter the title
                    content: window.prompt("title"),
                    done: false,
                    priority: 'medium'
                })
                console.log(errors, newTodo);
            }}>Create </button>

            <ul>
                {todos.map((todo) => (
                    <li key={todo.id}>{todo.content}</li>
                ))}
            </ul>
        </div>
    );
}

ポイントを解説しますね👍

<button onClick={async () => {
    // create a new Todo with the following attributes
    const { errors, data: newTodo } = await client.models.Todo.create({
        // prompt the user to enter the title
        content: window.prompt("title"),
        done: false,
        priority: 'medium'
    })
    console.log(errors, newTodo);
}}>Create </button>

あたらしいアイテムの作成はclient.models.Todo.createでできます.
型安全が保証されていますし,フロントエンド作成者がもうSwaggerを見ながらポチポチやらなくてもOKですね!

試しに追加してみましょう!

追加されました!

サンドボックス環境もチェックしてみましょう!

AWS AppSyncをみてみてください!

はいはい,出来上がってますね〜笑
データはAmazon DynamoDBによって永続化されます!

DynamoDBをチェックしてみると...

はい!たしかにありますね!

ちなみに,AWS AppSyncはサーバーレスのGraphQL APIを簡単に構築できるやつです!
もうちょっと深堀りしましょうか!
AWS Lambdaをみてみましょうか!

いっぱい出来てますねぇ!
amplify-nextamplifygen2-o-TableManagerOnEventHandl-*をみてみましょうか!

これはDynamo DBにたいしてCRUDをする関数ですね!

と,このように,Amplifyを使うとGraphQL APIを簡単につくってくれるわけですね!

ちなみに,サンドボックス環境は起動中のシェルを終了するときに,削除されます!
超便利ですね♥

いざデプロイ

さて,デプロイしていきましょう!
AWS Amplifyのいいところは,CI/CDも自動で構築してくれることですね!

リモートレポジトリにこれらをアップしていきます!
GitHub,AWS CodeCommit,GitLab,Bitbucketなどお好きなものをチョイスして使うことができます!
ということなので,適当にGithubレポを作成しましょう!

あとはここにpushしていきます!

git init
git remote add origin https://github.com/omuni1234/amplify-gen2-todo.git
git branch -M main
git push -u origin main

ついかされました!

ということなので,デプロイしちゃいましょう!
AmplifyのページからAmplify Gen 2を試すを押してみます!

オプション2から,GitHubを選択しましょう!

そうするとGitHubの認証画面がでてくるので,ぽちぽちしていきましょう!

レポジトリを選択しましょう!

ちゃんとできていると,次へが押せるようになります!
ここらへんが大丈夫なことを確認してみてくださいね👍

環境変数なども登録できます!

そして保存してデプロイをおしましょう!

AWSのインフラ構築中...

できました!

あとはビルドとデプロイに時間がかかるので,ちょっとまちましょうか!

惜しいのが,まだ独自ドメインが対応してないんですよね...

まとめ

AWS Amplify Gen2をつかってみて,一言.
すげーーーー楽!
サンドボックス環境で,プロダクトの挙動を逐一チェックしながら開発でき
しかもそのままデプロイできる!というのは開発者にとって圧倒的なストレスフリーでした笑

しかもサーバーレスだから,我々のようなまとまった資金を用意するのが難しい団体でも始められると!

はやく登場するのを,こころより楽しみにしています👍👍👍

Discussion