Vite + React + PostgreSQL環境の作り方

Vite + React + PostgreSQL環境の作り方

フロントエンド開発だけでなく、APIやDBまで含めて動かしたいケースは多くなっています。

しかし、フロントエンドエンジニアで「Vite + React」の環境は構築できても、「Vite + React + PostgreSQL」環境は構築できない人が多いのが実情です。

この記事では、Vite + React + PostgreSQLを組み合わせた開発環境をなるべく簡単に構築する方法を解説します。

※ Reactから直接PostgreSQLに接続することはできないため、必ずAPIサーバー(例: Express)を間に挟む構成にしています。

Dockerをインストールして起動する

PostgreSQLをローカルに簡単に立てるためにDockerを使用します。

Dockerの公式サイトからDocker Desktopをインストールして起動するだけでOKです。

起動するとメニューバーにDockerのアイコンが表示されます。

メニューバーのDockerのアイコン

Docker Desktopをダウンロードする

Vite + ReactとDockerの環境を準備

Vite + React + PostgreSQL環境は以下のディレクトリ構成で作成します。

my-app/
├─ docker-compose.yml
├─ package.json
├─ pnpm-workspace.yaml
├─ client/ ← Vite + React
└─ server/ ← Express

まず最初にmy-appディレクトリを作成して移動します。

mkdir my-app
cd my-app

次にVite + Reactをインストールします。

pnpm create vite@latest client --template react-ts

「Install with pnpm and start now?」が表示されるので、Yesを選択してインストールします。

Install with pnpm and start now?

インストールが完了すると起動してlocalhostのURLからアクセスできますが、まだブラウザで確認しないので「control + C」で起動を停止します。

「cd client」で移動してReact Compilerをインストールします。

現在のReactを使用する開発環境ではメモ化を自動化するReact Compilerが必要不可欠なので設定しておきます。

cd client
pnpm add -D babel-plugin-react-compiler @rolldown/plugin-babel

React Compilerの設定を有効にするために、vite.config.tsも以下のように変更します。

vite.config.ts
import { defineConfig } from 'vite'
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    react(),
    babel({ presets: [reactCompilerPreset()] }),
  ],
  server: {
    proxy: {
      '/api': 'http://localhost:3001',
    },
  },
})

client内のApp.tsxは fetch('/api/users') でユーザーの一覧を取得するページにします。

App.tsx
import { useEffect, useState } from 'react'

type User = {
  id: number
  name: string
}

export default function App() {
  const [users, setUsers] = useState<User[]>([])

  useEffect(() => {
    const fetchUsers = async () => {
      const res = await fetch('/api/users')
      const data: User[] = await res.json()
      setUsers(data)
    }

    fetchUsers()
  }, [])

  return (
    <main>
      <h1>Users</h1>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </main>
  )
}

ルートディレクトリにpnpm-workspace.yamlを作成します。

pnpm-workspace.yaml
packages:
  - 'client'
  - 'server'

ルートディレクトリにpackage.jsonも作成します。

「pnpm dev」を実行するとDockerとViteの両方が起動するようにしています。

package.json
{
  "name": "my-app",
  "private": true,
  "scripts": {
    "dev": "docker compose up -d && pnpm -r --parallel --stream dev",
    "db:down": "docker compose down"
  }
}

PostgreSQLをDockerで起動するために、ルートディレクトリにdocker-compose.ymlを作成します。

docker-compose.yml
services:
  db:
    image: postgres:17
    container_name: my_postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: apppass
      POSTGRES_DB: appdb
    ports:
      - '5433:5432'
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

サーバーの環境を作成する

まず、以下のコマンドでルートディレクトリにserverディレクトリを作成して、Expressサーバーを作成します。

mkdir server
cd server
pnpm init
pnpm add express pg cors dotenv
pnpm add -D typescript tsx @types/node @types/express @types/pg @types/cors

serverディレクトリ内のpackage.jsonのscriptsに「"dev": "tsx watch src/index.ts"」を追加します。

package.json
{
  // 略
  "scripts": {
    "dev": "tsx watch src/index.ts"
  }
  // 略
}

serverディレクトリ内にtsconfig.jsonを作成します。

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true
  },
  "include": ["src"]
}

serverディレクトリ内に.envを作成します。

.envにはデータベースに接続するURLが書かれています。

.env
DATABASE_URL=postgresql://appuser:apppass@localhost:5433/appdb

serverディレクトリ内にsrcディレクトリを作成して、その中にdb.tsとindex.tsを作成して、GET APIが動作するようにします。

db.ts
import pg from 'pg'
import dotenv from 'dotenv'

dotenv.config()

const { Pool } = pg

export const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
})
index.ts
import express from 'express'
import cors from 'cors'
import dotenv from 'dotenv'
import { pool } from './db'

dotenv.config()

const app = express()
const port = Number(process.env.PORT || 3001)

app.use(cors())
app.use(express.json())

app.get('/api/users', async (_req, res) => {
  const result = await pool.query('SELECT id, name FROM users ORDER BY id')
  res.json(result.rows)
})

app.listen(port, () => {
  console.log(`server running on http://localhost:${port}`)
})

以上の手順が完了したら、ルートディレクトリで「pnpm dev」を実行すると、Dockerおよびフロントとバックエンドのサーバーが起動します。

pnpm dev

この時点ではPostgreSQLにデータがないため、何も表示されません。

最後にデータを追加しましょう。

PostgreSQLのテーブル作成

以下のコマンドをターミナルで実行してPostgreSQLに入ります。

docker exec -it my_postgres psql -U appuser -d appdb

PostgreSQLに接続すると、表示がこのように変わります。

$ docker exec -it my_postgres psql -U appuser -d appdb
psql (17.9 (Debian 17.9-1.pgdg13+1))
Type "help" for help.

appdb=#

この状態でSQL文を実行してテーブルを追加します。

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

INSERT INTO users(name) VALUES
('Tanaka'),
('Suzuki');
$ docker exec -it my_postgres psql -U appuser -d appdb
psql (17.9 (Debian 17.9-1.pgdg13+1))
Type "help" for help.

appdb=#

appdb=# は exit; で終了できます。

以上の手順が完了していれば、http://localhost:5173/ にアクセスした際に画面上にTanakaとSuzukiが表示されているはずです。

ブラウザでのAPI表示

TanakaとSuzukiが何回も表示される場合はデータが重複しているので、余分なデータは以下のようにDELETEでidを指定して削除してください。

appdb=# DELETE FROM users
WHERE id IN (3, 4);
DELETE 2

まとめ

Vite + React + PostgreSQLの開発環境は、一見すると難しそうに見えますが、Dockerを活用することで比較的シンプルに構築できます。

この構成にしておくことで、実務に近い形で開発を進めることができ、将来的にAWSやVPSなどへデプロイする際にもスムーズに移行できます。

フロントエンドエンジニアでもバックエンドやDBを含めた環境を扱えるようになると、開発の幅が大きく広がるので、ぜひこの構成をベースに応用してみてください。