はじめに
バイブコーディングでReact / Express アプリを作っていきます
開発環境
- MacBook Air M1, 2020
- Cursor 2.1.50
- nvm 0.39.7
- Node.js 22.4.0
フォルダ構成
frontend: Vite + React + TypeScript のフロントエンド
backend: Express + TypeScript のバックエンド
導入
💬 frontendフォルダに vite, react, ts アプリをセットアップして
cd frontend
npm create vite@latest . -- --template react-ts
> npx
> create-vite . --template react-ts
│
◇ Use rolldown-vite (Experimental)?:
│ No
│
◇ Install with npm and start now?
│ No
│
◇ Scaffolding project in /frontend...
│
└ Done. Now run:
npm install
npm run dev
フロントエンドの起動
npm install
npm run dev
💬 backendにexpress+tsのバックエンドAPIをセットアップして
{
"name": "backend",
"version": "1.0.0",
"description": "Express + TypeScript Backend API",
"main": "dist/index.js",
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"type-check": "tsc --noEmit"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/cors": "^2.8.17",
"@types/node": "^20.10.0",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022"],
"moduleResolution": "node",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
import express, { Request, Response } from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3001;
// ミドルウェア
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// ルート
app.get('/', (req: Request, res: Response) => {
res.json({ message: 'Express + TypeScript API is running!' });
});
app.get('/api/health', (req: Request, res: Response) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// エラーハンドリング
app.use((err: Error, req: Request, res: Response, next: any) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
// 404ハンドリング
app.use((req: Request, res: Response) => {
res.status(404).json({ error: 'Route not found' });
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
PORT=3001
NODE_ENV=development
node_modules/
dist/
.env
*.log
.DS_Store
cd backend
npm install
npm run dev
http://localhost:3001 にアクセス

http://localhost:3001/api/health にアクセス

💬 フロントエンドでこのhealth APIを実行するだけのページにして
1.frontend/src/App.tsx - health APIを呼び出すページ
2.frontend/vite.config.ts - プロキシ設定(オプション)
💬 Basic認証を追加して
1.バックエンドの変更 (backend/src/index.ts)
2.フロントエンドの変更 (frontend/src/App.tsx)
💬 ログインページを追加して
frontend/src/App.tsx - ログイン機能を追加
💬 ユーザー名とパスワードを間違えた時に、ポップアップのログインが表示されてしまってるよ

💬 ページ幅を100%使うようにして @frontend/src/App.css @frontend/src/App.tsx @frontend/src/index.css
💬 認証周りのセキュリティは大丈夫かな?
1. localStorageにパスワードを保存しない(セッショントークンやJWTのみ保存)
2. 環境変数が必須になるようバリデーションを追加
3. HTTPSを必須化、またはJWT/Session認証へ移行
4. CORSを適切に制限(特定のオリジンのみ許可)
5. X-Requested-Withに依存しない実装に変更
6. 本番環境では詳細なエラー情報を返さない
💬 JWTトークンを使用するように変更して
1.バックエンドにjsonwebtokenパッケージを追加
2.バックエンドの認証ロジックをJWTに変更(ログインエンドポイント追加、認証ミドルウェア変更)
3.フロントエンドをJWTトークンを使用するように変更(パスワード保存を削除、トークンのみ保存)
💬 フロントエンドとバックエンドの起動方法をREADMEに書いて
# my-react-express-ngrok
React + Express + TypeScript のフルスタックアプリケーション
## プロジェクト構成
```
my-react-express-ngrok/
├── frontend/ # React + TypeScript + Vite
└── backend/ # Express + TypeScript
```
## セットアップ
### バックエンドのセットアップ
1. バックエンドディレクトリに移動
```bash
cd backend
```
2. 依存関係をインストール
```bash
npm install
```
3. 環境変数ファイルを作成(オプション)
```bash
# .env ファイルを作成
PORT=3001
AUTH_USERNAME=admin
AUTH_PASSWORD=password
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=24h
```
デフォルト値:
- PORT: 3001
- AUTH_USERNAME: admin
- AUTH_PASSWORD: password
- JWT_SECRET: your-secret-key-change-in-production(本番環境では必ず変更してください)
- JWT_EXPIRES_IN: 24h
**重要**: 本番環境では以下の環境変数が必須です:
- `JWT_SECRET`: 強力な秘密鍵を設定してください
- `AUTH_USERNAME`: 認証用のユーザー名
- `AUTH_PASSWORD`: 認証用のパスワード
### フロントエンドのセットアップ
1. フロントエンドディレクトリに移動
```bash
cd frontend
```
2. 依存関係をインストール
```bash
npm install
```
## 起動方法
### バックエンドの起動
1. バックエンドディレクトリに移動
```bash
cd backend
```
2. 開発サーバーを起動
```bash
npm run dev
```
バックエンドAPIは `http://localhost:3001` で起動します。
### フロントエンドの起動
1. フロントエンドディレクトリに移動
```bash
cd frontend
```
2. 開発サーバーを起動
```bash
npm run dev
```
フロントエンドは `http://localhost:5173` で起動します。
## 使用方法
1. バックエンドとフロントエンドの両方を起動します
2. ブラウザで `http://localhost:5173` にアクセスします
3. ログインページで認証情報を入力します
- デフォルトのユーザー名: `admin`
- デフォルトのパスワード: `password`
4. ログイン成功後、JWTトークンが発行され、localStorageに保存されます
5. ログイン後、ダッシュボードからHealth APIをチェックできます
## 認証について
このアプリケーションはJWT(JSON Web Token)認証を使用しています。
### 認証フロー
1. ユーザーがログイン情報を入力
2. バックエンドの `/api/login` エンドポイントにPOSTリクエストを送信
3. 認証成功後、JWTトークンが発行される
4. トークンはフロントエンドのlocalStorageに保存される(パスワードは保存されません)
5. 以降のAPIリクエストでは、`Authorization: Bearer <token>` ヘッダーでトークンを送信
6. バックエンドでトークンを検証し、有効な場合のみリクエストを処理
### セキュリティ機能
- ✅ パスワードはlocalStorageに保存されません
- ✅ JWTトークンには有効期限が設定されています(デフォルト: 24時間)
- ✅ トークンは署名済みで、改ざんを検知できます
- ✅ 本番環境では環境変数の設定が必須です
## ビルド
### バックエンドのビルド
```bash
cd backend
npm run build
```
ビルド後のファイルは `dist/` ディレクトリに生成されます。
### フロントエンドのビルド
```bash
cd frontend
npm run build
```
ビルド後のファイルは `dist/` ディレクトリに生成されます。
## 技術スタック
### フロントエンド
- React 19
- TypeScript
- Vite
- CSS (Inline Styles)
### バックエンド
- Express
- TypeScript
- JWT認証(jsonwebtoken)
- CORS対応
- dotenv(環境変数管理)
## API エンドポイント
### 公開エンドポイント
- `GET /` - APIの状態確認
- `POST /api/login` - ログイン(JWTトークンを発行)
### 保護されたエンドポイント
- `GET /api/health` - ヘルスチェック(認証必須)
保護されたエンドポイントには、`Authorization: Bearer <token>` ヘッダーが必要です。
クローンした人がすぐに始められるよう、環境変数の例(.env.example)を追加しておきましょう。
PORT=3001
AUTH_USERNAME=admin
AUTH_PASSWORD=password
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=24h
お疲れ様でした。
ngrokで外部に公開する方法
※新規アカウントでエンドポイントが2つ作成されなかったのでできないかも
ngrokのインストール
brew install ngrok
ngrokのauthtokenを設定
ngrok config add-authtoken YOUR_AUTHTOKEN
ngrok.ymlファイルの作成
version: 3
agent:
authtoken: your_authtoken
tunnels:
frontend:
addr: 5173
proto: http
backend:
addr: 3001
proto: http
ルートに.gitignoreを作成
.DS_Store
ngrok.yml
フロントエンドの環境変数設定(frontend/.env)
VITE_API_BASE_URL=your-ngrok-backend-url
.gitignoreの末尾に.envを追加
.env
フロントエンドコードの修正(App.tsx)
App.tsxの修正: 環境変数 VITE_API_BASE_URL を使用するように変更
vite.config.tsの修正
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
allowedHosts: [
'.ngrok-free.app',
'.ngrok.app',
'localhost'
],
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true,
}
}
}
})
ngrokを起動
ngrok start --all --config ngrok.yml
Forwarding https://xxxxxxxxxxxx.ngrok-free.app -> http://localhost:5173
Forwarding https://yyyyyyyyyyyy.ngrok-free.app -> http://localhost:3001
バックエンドの起動
cd backend
npm run dev
フロントエンドの.envのVITE_API_BASE_URLをngrokのバックエンドのURLに変更
VITE_API_BASE_URL=https://yyyyyyyyyyyy.ngrok-free.app
フロントエンドの起動
cd frontend
npm run dev
https://xxxxxxxxxxxx.ngrok-free.app にアクセス
プロジェクトは通常のクライアントサイドReactアプリのため、脆弱性CVE-2025-55182(React2Shell)の影響は受けませんが、セキュリティのベストプラクティスとして修正しました。
"dependencies": {
"react": "^19.2.1",
"react-dom": "^19.2.1"
},








