in these days, i prepare the developing envioromnent with aws amplify gen2 and next.js. this is the summary.
install AWS CLI on [https://awscli.amazonaws.com/AWSCLIV2.msi]
Set up authentication credentials
aws configure
AWS Access Key ID: あなたのアクセスキー
AWS Secret Access Key: あなたのシークレットキ
Default region name: 例 ap-northeast-1 (東京)
Default output format: json (Enter でOK)
confirm AWS CLI
aws --version
Install node.js on https://nodejs.org/ja/download
Move to current directory
cd (current directory)
Recreate npm Project / Project Setup
npx create-next-app@latest
Install Amplify Backend and Frontend SDKs in the Next.js Project Root Directory
npm install @aws-amplify/backend
npm install @aws-amplify/data-schema
npm install aws-amplify
npm install @aws-amplify/ui-react
Create Directories and Files
mkdir amplify
mkdir amplify\data
mkdir amplify\auth
type nul > amplify\backend.ts
type nul > amplify\data\resource.ts
type nul > amplify\auth\resource.ts
Open the created amplify/backend.ts file and add the following content:
// amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';
// 認証リソースをインポート
// aws公式サイトを見ても.tsがついていないが、npx ampx sandobxのときにtsがついていないとエラーが出る
import { auth } from './auth/resource.ts';
import { data } from './data/resource.ts';
// The Amplify backend definition.
defineBackend({
auth, // ここにインポートした 'auth' を追加
data, // ここにインポートした 'data' を追加
});
Open the created amplify/data/resource.ts file and add the following content:
// @aws-amplify/backend から必要なモジュールをインポートします。
// a: スキーマ定義のためのヘルパー (例: a.model, a.string)
// defineData: データリソース全体を定義するための関数
// type ClientSchema: フロントエンドでの型付けに使用されるユーティリティ型
import { a, defineData, type ClientSchema } from '@aws-amplify/backend';
// データのスキーマを定義します。これはGraphQLスキーマの定義に相当します。
// a.modelと定義するだけで、query(取得)、mutation(更新)も自動で定義。
const schema = a.schema({
// 'Todo' という名前のデータモデルを定義します。
// これは AWS DynamoDB のテーブルに対応します。
Todo: a.model({
// 'content' というフィールドを定義します。型は文字列 (String) です。
content: a.string(),
// 'isDone' というフィールドを定義します。型は真偽値 (Boolean) です。
isDone: a.boolean()
})
// このモデルに対するアクセス許可(認可ルール)を定義します。
// ここでは、パブリックな API キー (publicApiKey) を持つユーザー(クライアント)にアクセスを許可しています。
.authorization(allow => [allow.publicApiKey()])
});
// Used for code completion / highlighting when making requests from frontend
// フロントエンド (Amplify クライアント) でリクエストを行う際、
// スキーマの型補完やハイライト表示に使用するためにエクスポートされる型です。
export type Schema = ClientSchema<typeof schema>;
// defines the data resource to be deployed
// デプロイされるデータリソース(AppSync APIとDynamoDBテーブルなど)を定義し、設定します。
export const data = defineData({
// 上で定義した GraphQL スキーマを設定します。
schema,
// 認証モードを設定します。
authorizationModes: {
// デフォルトの認証モードとして 'apiKey' を設定します。
defaultAuthorizationMode: 'apiKey',
// API Key 認証モードの詳細設定です。
apiKeyAuthorizationMode: {
// API キーの有効期限を30日に設定します。
expiresInDays: 30
}
}
});
Open the created amplify/auth/resource.ts file and add the following content:
// amplify/auth/resource.ts
import { defineAuth, secret } from "@aws-amplify/backend";
/**
* The Amplify Auth resource definition.
*/
export const auth = defineAuth({
loginWith: {
// ユーザー名とパスワードによるログインを有効化
email: true,
},
});
Create Directories and Files
type nul > app\providers.tsx
type nul > app\useAmplifyClient.ts
type nul > components\TodoList.tsx
Open the app/layout.tsx file and add the following content:
// app/layout.tsx
// ★ "use client" はここに書かない(Server Componentのままにする)
import './globals.css' // グローバルCSSのインポート
// 新しく作成した Client Component ラッパーをインポート
import Providers from '../app/providers'
//ここは、ログイン前の画面のroot
export default function RootLayout({
children,
}: {
children: React.ReactNode // ★ childrenを受け取る形式が必須
}) {
return (
<html lang="ja">
<body>
{/* Providers (Client Component) でラップ */}
<Providers>{children}</Providers>
</body>
</html>
)
}
Open the app/page.tsx file and add the following content:
// app/dashboard/page.tsx
'use client'
import { Authenticator } from '@aws-amplify/ui-react';
import TodoList from '../components/TodoList';// TodoListコンポーネントを切り出す
export default function DashboardPage() {
return (
<Authenticator>
{({ signOut, user }) => (
<main>
<h1>Hello, {user?.username}!</h1>
<TodoList /> {/* 👈 Todoリストコンポーネントを配置 */}
<button onClick={signOut}>サインアウト</button>
</main>
)}
</Authenticator>
);
}
Open the app/providers.tsx file and add the following content:
// app/providers.tsx
'use client' // ★ これをファイルの先頭に記述!
import '@aws-amplify/ui-react/styles.css'
import { Amplify } from 'aws-amplify'
import outputs from '../amplify_outputs.json'
//ここで、Providersコンポーネントを定義している
//React.ReactNodeとは何だろうか、上記のimoprtのどれかに要素が入っているのだろうが
// ここで設定を一回だけ実行する
// SSR環境での二重実行を防ぐための簡易チェック
Amplify.configure(outputs)
export default function Providers({ children }: { children: React.ReactNode }) {
// ここでContext Providerなどを提供してもOK
return <>{children}</>
}
Open the app/useAmplifyClient.ts file and add the following content:
// src/hooks/useAmplifyClient.ts (例)
'use client'
import { useState, useEffect } from 'react';
import { generateClient, Client } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // 👈 パスに注意
// クライアントを生成し、その状態を返すカスタムフック
export function useAmplifyClient() {
// clientの状態を管理し、初期値はnullにする
const [client, setClient] = useState<Client<Schema> | null>(null);
// コンポーネントがマウントされたら、クライアントを生成する
useEffect(() => {
// Amplifyの設定が Providers.tsx で完了していることを期待
setClient(generateClient<Schema>());
}, []);
return client; // client (または null) を返す
}
Open the components/TodoList.tsx file and add the following content:
// app/dashboard/TodoList.tsx (または page.tsx)
'use client'
import { useState, useEffect } from "react";
// スキーマはフック内で使用されるため、ここでは不要(フックが内部でインポートしている)
// import type { Schema } from "../../amplify/data/resource";
// import { generateClient, Client } from "aws-amplify/data";
// 🚨 【重要】Todoリストの処理に必要なSchema型だけはインポートし直す
import type { Schema } from "@/amplify/data/resource";
import { useAmplifyClient } from '@/app/useAmplifyClient'; // 👈 カスタムフックをインポート
export default function TodoList() {
// 🎉 クライアントの初期化ロジックをフックに任せる
// clientにはバックエンドの全情報が入っている
// しかし、clientは読み込み時にnull ->値に変わるが、
// createのタイミングなどそれ以外では不変
const client = useAmplifyClient();
// todosがメモの配列、それをsetTodos関数で更新する
// ()内は初期値で、今回は空配列[]
// <>内はtypescriptの型定義
// Schemaはamplify/data/resource.tsで定義したもの
// プロパティでTodo,typeをたどっている
// ただし、あくまでtodosのリストなので最後に[]をつけている
// でも結局は配列のどの要素も型は同じ
// 型にも引数にも[]をつけて単一の値と区別する
const [todos, setTodos] = useState<Schema["Todo"]["type"][]>([]);
// 以降、ロジックはシンプルになる
// データ取得関数、データベースからデータを読み込む
const fetchTodos = async () => {
if (!client) return;
// await client.models.Todo.list()で返されるオブジェクトのイメージ
// {
// data: [ // 実際のTodoの配列
// { id: "1", status: "牛乳を買う", isDone: false },
// { id: "2", status: "メールをチェック", isDone: true }
// ],
// // その他の情報(トークンなど、データ取得のメタ情報)
// nextToken: null
// }
// 分割代入 上記のうち、dataプロパティだけを取得する
// だからamplify/data/resource.tsで正しく定義する必要あり
const { data: items } = await client.models.Todo.list();
// 以下でitemsを使った状態の更新が行われ、
// 状態が更新されると、UIの更新が行われる(reactの基本機能)
setTodos(items);
};
// 画面読み込み時クライアントがセットされた後、データをフェッチする
// clientが読み込み後にnull ->値に変化することに対応している
// useEffectはUIと関係ない処理の実行に使う
// 二個引数を取っている
// 一つ目は実行したい処理
// 二つ目がトリガー
// これは、読み込み時のclientがnull->値に変わった時の動作
// それ以降はclientはcreateの際も不変
useEffect(() => {
if (client) {
fetchTodos();
}
}, [client]);
// データ作成関数
const createTodo = async () => {
if (!client) return;
// client.models.Todo.createでデータベースにhttpリクエスト
// window.pronpt()はブラウザの標準機能の利用
await client.models.Todo.create({
content: window.prompt("Todo content?"),
isDone: false,
});
fetchTodos();
}
// クライアントが生成されるまでローディング表示をする
if (!client) {
return <div>データを読み込み中...</div>;
}
// {...}:JSX内でJavascriptの式を埋め込み
// key=idはUIには現れないが必須
return (
<div>
<button onClick={createTodo}>Add new todo</button>
<ul>
{todos.map(({ id, content }) => (
<li key={id}>{content}</li>
))}
</ul>
</div>
);
}
execute the sandbox
npx ampx sandbox
when finish sandbox, Ctrl+C, and then npx ampx sandbox delete
to conform hot reload of sandbox, check console of AWS AppSync Schema.
Top comments (0)