概要
Reactで作ったUIからNode.jsで構えてるAPIを叩き、RDS上のデータを検索して表示するサンプル。
「React RDS 接続」でググったら SpringBoot を使う例しか出てこなくて困ったので、備忘録として記事を作成した。
構成
Frontend (React) → Backend (Node.js + Express) → PostgreSQL(AWS RDS)
バックエンド
Node.js準備。expressとnode-postgresをインストール。
mkdir backend && cd backend
npm init -y
npm install express pg cors dotenv
バックエンドはindex.jsのみ作成。フロントから受け渡された検索クエリをもとに、RDSを検索、結果を返答する。
ここではRDS上のユーザーをIDで検索する想定。
index.jsx
const express = require('express');
const { Pool } = require('pg');
const cors = require('cors');
require('dotenv').config();
const app = express();
const port = 4000;
const pool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_DATABASE,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT,
});
app.use(cors());
app.get('/api/users', async (req, res) => {
// 検索クエリをセット
const search = req.query.search;
let query = 'SELECT * FROM '+ process.env.DB_TABLE;
const params = [];
if (search) {
query += ' WHERE id = $1';
params.push(`%${search}%`);
}
try {
// RDSインスタンスに接続
const result = await pool.query(query, params);
res.json(result.rows);
} catch (error) {
console.error(error);
res.status(500).send('Server Error');
}
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
.env
DB_USER=username
DB_PASSWORD=password
DB_HOST=rds_endpoint
DB_PORT=5432
DB_DATABASE=dbname
DB_TABLE=tablename
想定しているRDSの中身
RDSの中身
[
{
"id": 1,
"name": "田中太郎",
"email": "tanaka@example.com"
},
{
"id": 2,
"name": "鈴木花子",
"email": "suzuki@example.com"
}
]
フロントエンド
セットアップ。Vite使用。tailwindはv4から色々変わっているので注意。
npm create vite@latest frontend -- --template react
cd frontend
npm install
npm install axios
npm install tailwindcss @tailwindcss/vite
メイン画面作成。検索ボックスと検索結果を表示するだけのシンプル構成。
UIセンス皆無なのでcssは生成AIにお任せした。
src/components/UserList.jsx
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const UsersList = () => {
const [users, setUsers] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = (query = '') => {
axios.get(`http://localhost:4000/api/users?search=${query}`)
.then(res => setUsers(res.data))
.catch(err => console.error(err));
};
const handleSearch = (e) => {
e.preventDefault();
fetchUsers(searchTerm);
};
return (
<div className="min-h-screen bg-gray-100 py-10 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto bg-white shadow-xl rounded-2xl overflow-hidden">
<div className="px-6 py-5 bg-gradient-to-r from-blue-500 to-indigo-600">
<h2 className="text-2xl font-bold text-white">ユーザー一覧</h2>
</div>
{/* 検索フォーム */}
<form onSubmit={handleSearch} className="mb-4 flex">
<input
type="text"
placeholder="IDを入力して検索..."
className="flex-grow px-4 py-2 border border-gray-300 rounded-l-md focus:outline-none"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<button
type="submit"
className="px-4 py-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600"
>
検索
</button>
</form>
{/* 検索結果表示 */}
<div className="p-6">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-600 uppercase tracking-wider">
ID
</th>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-600 uppercase tracking-wider">
名前
</th>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-600 uppercase tracking-wider">
メール
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{users.map(user => (
<tr key={user.id} className="hover:bg-gray-50">
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-700">
{user.id}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{user.name}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-blue-500">
{user.email}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
);
};
export default UsersList;
tailwind使用準備。設定を追加する。
詳細は公式ドキュメント参照。
App.css
+ @import "tailwindcss";
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
App.jsx
import React from 'react';
+ import './App.css';
import UsersList from './components/UsersList';
function App() {
return (
<UsersList />
);
}
export default App;
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+ import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
react(),
+ tailwindcss(),
],
})
実行結果
参考:フォルダ構成
記述に以下を使わせてもらった。便利。
.
├── backend/
│ ├── index.js
│ └── .env
└── frontend/
├── src/
│ ├── components/
│ │ └── UserList.jsx
│ ├── App.jsx
│ └── App.css
└── vite.config.js