AWS Amplify Gen2でRDS(MySQL)のCRUD操作を行う
はじめに
一覧を表示(読み込み)、登録と削除
更新
事前にRDSで下記のDB(myapp
)とテーブル(users
)が作成されているものとします。
フロントはNext.js("next": "13.5.4"
)です。
SET CHARSET UTF8;
CREATE DATABASE IF NOT EXISTS myapp;
USE myspp;
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
INSERT INTO users (first_name, last_name) VALUES ('山田', '太郎'), ('佐藤', '花子');
シークレット設定
RDSインスタンスが以下の場合は
ホスト名: mydatabase.c12345abcde.ap-northeast-1.rds.amazonaws.com
ポート: 3306
データベース名: myapp
ユーザー名: admin
パスワード: password
MySQLの接続文字列形式は下記のようになります。
mysql://admin:password@mydatabase.c12345abcde.ap-northeast-1.rds.amazonaws.com:3306/myapp
下記コマンドを実行すると、接続文字列の入力が求められますので登録してください。
npx ampx sandbox secret set SQL_CONNECTION_STRING
設定内容を間違えた場合は、下記を参考に設定し直してください。
データベーススキーマの生成
シークレットをもとにamplify/data/schema.sql.ts
というデータベーススキーマを生成します。
npx ampx generate schema-from-database --connection-uri-secret SQL_CONNECTION_STRING --out amplify/data/schema.sql.ts
生成したスキーマは編集せずにamplify/data/resource.ts
でインポートして使用してください。
バックエンドデータアクセスの設定
下記のように作成します。
import { defineData, type ClientSchema } from '@aws-amplify/backend';
import { schema as generatedSqlSchema } from './schema.sql';
const schema = generatedSqlSchema
.authorization((allow) => allow.authenticated())
.renameModels(() => [['users', 'Users']]);
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'userPool',
},
});
フロントの作成
'use client';
import type { Schema } from '@/amplify/data/resource';
import outputs from '@/amplify_outputs.json';
import { Authenticator, ThemeProvider } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import { Amplify } from 'aws-amplify';
import { generateClient } from 'aws-amplify/data';
import { useEffect, useState } from 'react';
import styles from './page.module.css';
Amplify.configure(outputs);
const client = generateClient<Schema>();
export default function App() {
const [users, setUsers] = useState<Array<Schema['Users']['type']>>([]);
const listUsers = () => {
const sub = client.models.Users.observeQuery().subscribe({
next: (data) => {
setUsers([...data.items]);
},
error: (err) => {
console.error('ユーザーの取得に失敗しました。:', err);
},
});
return () => sub.unsubscribe();
};
useEffect(() => {
listUsers();
}, []);
const createUser = () => {
const firstName = window.prompt('姓を入力してください。');
const lastName = window.prompt('名を入力してください。');
if (firstName && lastName) {
client.models.Users.create({
id: 3,
first_name: firstName,
last_name: lastName,
})
.then(() => listUsers())
.catch((error) =>
console.error('ユーザー登録ができませんでした。:', error)
);
}
};
const updateUser = (id: number) => {
const updateFirstName = window.prompt('姓を入力してください。');
const updateLastName = window.prompt('名を入力してください。');
if (updateFirstName && updateLastName) {
client.models.Users.update({
id: id,
first_name: updateFirstName,
last_name: updateLastName,
});
setUsers(users.filter((user) => user.id !== id));
}
};
const deleteUser = (id: number) => {
client.models.Users.delete({ id });
setUsers(users.filter((user) => user.id !== id));
};
return (
<ThemeProvider>
<Authenticator>
{({ signOut, user }) => (
<main>
<h1>{user?.signInDetails?.loginId}で ログインしています。</h1>
<h1>ユーザーリスト</h1>
<button onClick={createUser} className={styles['button']}>
ユーザーを作成する
</button>
<ul>
{users.map((item) => (
<li key={item.id} className={styles['list-item']}>
<span className={styles['name']}>
{item.first_name} {item.last_name}
</span>
<button
onClick={() => updateUser(item.id)}
className={styles['button']}
>
Update
</button>
<button
onClick={() => deleteUser(item.id)}
className={styles['button']}
>
Delete
</button>
</li>
))}
</ul>
<button onClick={signOut} className='button'>
Sign out
</button>
</main>
)}
</Authenticator>
</ThemeProvider>
);
}
Window.prompt()
ユーザーにテキスト入力を求めるダイアログボックスを表示します。
第一引数はユーザーに表示するメッセージ、第二引数はテキスト入力フィールドのデフォルト値(省略可能)です。
ユーザーが「OK」をクリックすると、入力されたテキストが関数から返されます。ユーザーが「キャンセル」をクリックすると、null
が返されます。
スタイル
.name {
margin-right: auto;
}
.list-item {
display: flex;
align-items: center;
}
.button {
margin-right: 10px;
}
不明な点
id INT AUTO_INCREMENT PRIMARY KEY
という設定にも関わらず、手動でid
を設定しないとエラーが発生してしまい、解決方法が分からなかったです。(解決法として、データの数を数えて次のid
として+1する方法する方法では、同時にアクセスがあった時にエラーになってしまう...)
個別にSQLを実行する場合の記述がわからなかったので、ご存知の方は教えていただけると助かります。
参考にさせていただきました
Property 'addToSchema' does not exist on type 'RDSModelSchema
addToSchema
を使用してカスタムクエリを設定しようとするとエラーになりました。
終わりに
何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉
Discussion