はじめに
AWS Amplify Advent Calendar 2024 の7日目の記事です。
個人的に Amplify が好きなので、これまで(*1)も Amplify 関連記事を書いてきました。今年は Amplify Gen2 のバックエンド周りの情報を整理していきたいと思います。
(*1)
Amplify Gen2 とは
Amplify は2024年5月に Gen1(第一世代)から Gen2(第二世代)に進化しました。Gen2 で進化したポイントは以下の4つです。
TypeScript フルスタック開発
フロントとバック両方を TypeScript, AWS CDK でコード化
Amplify コンソールで統合管理
ビルド、ホスティング、環境変数、シークレット、データ、認証、ストレージ
開発者別のサンドボックス環境
Gen1 ではチーム共有環境だったものが、開発者ごとの独立環境へ
Git ベースの環境管理
ブランチに応じた本番・ステージング・プレビュー環境
TypeScript によるフルスタック構成
フロントエンドとバックエンド両方を TypeScript で型安全・宣言的に定義していくことができます。
データモデル(DynamoDB)、API(AppSync)、認証・認可(Cognito)、ストレージ(S3)、関数(Lambda)を TypeScript で宣言的に定義すると AWS リソースが自動生成されます。また AWS CDK を TypeScript で記述した IaC でクラウドインフラを構築・管理できます。そして、このバックエンドと React, Vue.js などのモダンフロントエンドを接続することができます。
手を動かして理解を深める
AWS Summit Japan 2024 で Gen2 に触発されて書いた記事の続きから手を動かして理解を深めていきたいと思います。
Auth
バックエンド
メールログインでリソース定義します。
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,
},
});
フロントエンド
UI コンポーネントで Authenticator
が用意されているので利用します。
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
function App() {
...省略...
return (
<Authenticator>
{({ signOut }) => (
<main>
<h1>My todos</h1>
...省略...
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
);
}
export default App;
Data
バックエンド
DynamoDB のデータモデル定義をします。
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";
const schema = a.schema({
Todo: a
.model({
content: a.string(),
})
.authorization((allow) => [allow.publicApiKey()]),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: "apiKey",
// API Key is used for a.allow.public() rules
apiKeyAuthorizationMode: {
expiresInDays: 30,
},
},
});
フロントエンド
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
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") });
}
function deleteTodo(id: string) {
client.models.Todo.delete({ id })
}
...データ操作はこの辺りの実装です....
return (
<Authenticator>
{({ signOut }) => (
<main>
<h1>My todos</h1>
...データ操作はこの辺りの実装です....
<button onClick={createTodo}>+ new</button>
<ul>
{todos.map((todo) => (
<li
key={todo.id}
onClick={() => deleteTodo(todo.id)}
>{todo.content}</li>
))}
</ul>
...データ操作はこの辺りの実装です....
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
);
}
export default App;
Storage
バックエンド
S3 のリソース設定をします。
import { defineStorage } from '@aws-amplify/backend';
export const storage = defineStorage({
name: 'amplifyTeamDrive',
access: (allow) => ({
'profile-pictures/{entity_id}/*': [
allow.guest.to(['read']),
allow.entity('identity').to(['read', 'write', 'delete'])
],
'picture-submissions/*': [
allow.authenticated.to(['read', 'write']),
allow.guest.to(['read', 'write'])
],
})
});
フロントエンド
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import { useEffect, useState } from "react";
import { uploadData } from 'aws-amplify/storage';
function App() {
...Storage操作はこの辺り...
const [file, setFile] = useState<File | undefined>();
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (files) {
setFile(files[0]);
}
};
const handleUpload = () => {
if (file) { // undefined チェック
uploadData({
path: `picture-submissions/${file.name}`,
data: file,
})
alert(`${file.name} がアップロードされました`)
} else {
alert(`ファイルが選択されていません`)
};
};
...Storage操作はこの辺り...
return (
<Authenticator>
{({ signOut }) => (
<main>
<h1>My todos</h1>
...Storage操作はこの辺り...
<div>
<input type="file" onChange={handleChange} />
<button
onClick={handleUpload}>
Upload
</button>
</div>
...Storage操作はこの辺り...
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
);
}
export default App;
おわりに
今回は Auth, Data, Storage 周りをさらっと触りました。他にも Function 等もあり、まだまだ深堀りがいがありそうですね。
それでは良いお年を。