Cursorでフルスタック開発する実践ガイド - React・Node.js・Pythonで構築する現代的なWebアプリ

Tech Trends AI
- 8 minutes read - 1519 wordsCursorでフルスタック開発する実践ガイド
AIを活用した開発環境が注目される中、Cursorはその最前線に立つエディタとして多くの開発者に支持されています。本記事では、Cursorを使ってReact、Node.js、Pythonを組み合わせたフルスタック開発を実践的に学んでいきます。
目次
- フルスタック開発における現代的な技術スタック
- Cursorでのプロジェクト設計と準備
- バックエンドAPI開発(Node.js + Express)
- フロントエンド開発(React + TypeScript)
- データ処理・分析層(Python)
- 統合とデプロイメント戦略
- Cursorの活用テクニック
- トラブルシューティングとベストプラクティス
フルスタック開発における現代的な技術スタック
技術選定の理由
フロントエンド: React + TypeScript
- コンポーネントベースの再利用性
- TypeScriptによる型安全性
- 豊富なエコシステム
バックエンド: Node.js + Express
- JavaScriptの統一言語環境
- 高いI/O処理性能
- RESTful API構築の簡単さ
データ処理: Python
- 機械学習・データサイエンスライブラリの充実
- Pandas、NumPy、scikit-learn等の活用
- API連携によるマイクロサービス構成
アーキテクチャ設計
┌─────────────┐ HTTP/REST ┌─────────────┐
│ React App │ ←──────────────→ │ Node.js API │
│ (Frontend) │ │ (Backend) │
└─────────────┘ └─────────────┘
│
HTTP/API
↓
┌─────────────┐
│ Python │
│ Data Layer │
└─────────────┘
Cursorでのプロジェクト設計と準備
プロジェクト構造の作成
Cursorのコマンドパレット(Ctrl+Shift+P)を開き、以下のプロジェクト構造を作成します:
fullstack-app/
├── frontend/ # React アプリケーション
│ ├── src/
│ ├── public/
│ └── package.json
├── backend/ # Node.js API サーバー
│ ├── src/
│ ├── routes/
│ └── package.json
├── data-processor/ # Python データ処理
│ ├── api/
│ ├── models/
│ └── requirements.txt
└── docker-compose.yml # 開発環境統合
Cursorワークスペース設定
.vscode/settings.json
{
"python.defaultInterpreterPath": "./data-processor/venv/bin/python",
"typescript.preferences.includePackageJsonAutoImports": "auto",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
バックエンドAPI開発(Node.js + Express)
セットアップとプロジェクト初期化
cd backend
npm init -y
npm install express cors helmet morgan dotenv
npm install -D nodemon typescript @types/node @types/express
TypeScriptによるAPI構築
src/app.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;
// Middleware
app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(express.json());
// Routes
app.get('/api/health', (req, res) => {
res.json({ status: 'OK', timestamp: new Date().toISOString() });
});
// User management routes
app.use('/api/users', require('./routes/users'));
app.use('/api/data', require('./routes/data'));
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
RESTful API エンドポイント設計
src/routes/users.ts
import { Router, Request, Response } from 'express';
import axios from 'axios';
const router = Router();
// ユーザー一覧取得
router.get('/', async (req: Request, res: Response) => {
try {
// Pythonデータ処理APIとの連携
const response = await axios.get('http://localhost:8000/process/users');
res.json({
success: true,
data: response.data,
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// ユーザー作成
router.post('/', async (req: Request, res: Response) => {
const { name, email, role } = req.body;
// バリデーション
if (!name || !email) {
return res.status(400).json({
success: false,
error: 'Name and email are required'
});
}
try {
const userData = {
id: Date.now().toString(),
name,
email,
role: role || 'user',
createdAt: new Date().toISOString()
};
// Pythonデータ処理APIに送信
await axios.post('http://localhost:8000/store/user', userData);
res.status(201).json({
success: true,
data: userData
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Failed to create user'
});
}
});
module.exports = router;
Cursorのコード生成活用
CursorのTab補完機能を使用して、API エンドポイントを効率的に生成:
- コメント駆動開発:機能をコメントで記述
- Tab補完:Cursorが実装を提案
- リファクタリング:AI支援でコード最適化
// TODO: データ分析API エンドポイントを作成
// - GET /api/analytics/summary - 全体サマリー取得
// - GET /api/analytics/trends - トレンドデータ取得
// - POST /api/analytics/query - カスタムクエリ実行
// Cursorがここから自動補完で実装を提案
フロントエンド開発(React + TypeScript)
React プロジェクト設定
cd frontend
npx create-react-app . --template typescript
npm install axios @types/axios styled-components @types/styled-components
npm install recharts react-router-dom @types/react-router-dom
コンポーネント設計とカスタムフック
src/hooks/useApi.ts
import { useState, useEffect } from 'react';
import axios from 'axios';
interface ApiState<T> {
data: T | null;
loading: boolean;
error: string | null;
}
export function useApi<T>(url: string): ApiState<T> {
const [state, setState] = useState<ApiState<T>>({
data: null,
loading: true,
error: null
});
useEffect(() => {
const fetchData = async () => {
try {
setState(prev => ({ ...prev, loading: true }));
const response = await axios.get<T>(`http://localhost:5000${url}`);
setState({
data: response.data,
loading: false,
error: null
});
} catch (error) {
setState({
data: null,
loading: false,
error: 'データの取得に失敗しました'
});
}
};
fetchData();
}, [url]);
return state;
}
ダッシュボードコンポーネント
src/components/Dashboard.tsx
import React from 'react';
import styled from 'styled-components';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
import { useApi } from '../hooks/useApi';
interface DashboardData {
summary: {
totalUsers: number;
activeUsers: number;
revenue: number;
};
trends: Array<{
date: string;
users: number;
revenue: number;
}>;
}
const DashboardContainer = styled.div`
padding: 20px;
max-width: 1200px;
margin: 0 auto;
`;
const MetricsGrid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
`;
const MetricCard = styled.div`
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
h3 {
margin: 0 0 10px 0;
color: #333;
}
.value {
font-size: 2em;
font-weight: bold;
color: #007bff;
}
`;
const ChartContainer = styled.div`
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
`;
export const Dashboard: React.FC = () => {
const { data, loading, error } = useApi<DashboardData>('/api/analytics/summary');
if (loading) return <div>読み込み中...</div>;
if (error) return <div>エラー: {error}</div>;
if (!data) return <div>データがありません</div>;
return (
<DashboardContainer>
<h1>ダッシュボード</h1>
<MetricsGrid>
<MetricCard>
<h3>総ユーザー数</h3>
<div className="value">{data.summary.totalUsers.toLocaleString()}</div>
</MetricCard>
<MetricCard>
<h3>アクティブユーザー</h3>
<div className="value">{data.summary.activeUsers.toLocaleString()}</div>
</MetricCard>
<MetricCard>
<h3>売上</h3>
<div className="value">¥{data.summary.revenue.toLocaleString()}</div>
</MetricCard>
</MetricsGrid>
<ChartContainer>
<h3>トレンドチャート</h3>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data.trends}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="users" stroke="#007bff" />
<Line type="monotone" dataKey="revenue" stroke="#28a745" />
</LineChart>
</ResponsiveContainer>
</ChartContainer>
</DashboardContainer>
);
};
データ処理・分析層(Python)
Python APIサーバー設定
data-processor/requirements.txt
fastapi==0.104.1
uvicorn==0.24.0
pandas==2.1.3
numpy==1.25.2
scikit-learn==1.3.2
pydantic==2.5.0
python-multipart==0.0.6
FastAPIによるデータ処理API
data-processor/main.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import json
from typing import List, Dict, Any
app = FastAPI(title="Data Processing API", version="1.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# データモデル
class User(BaseModel):
id: str
name: str
email: str
role: str
createdAt: str
class AnalyticsQuery(BaseModel):
metric: str
start_date: str
end_date: str
filters: Dict[str, Any] = {}
# メモリ内データストレージ(実際の開発ではDBを使用)
users_data = []
analytics_data = []
@app.get("/health")
async def health_check():
return {"status": "OK", "timestamp": datetime.now().isoformat()}
@app.post("/store/user")
async def store_user(user: User):
"""ユーザーデータを保存し、分析用データを生成"""
users_data.append(user.dict())
# 分析用データの生成
analytics_entry = {
"date": user.createdAt[:10],
"users": 1,
"revenue": np.random.uniform(1000, 5000),
"activity_score": np.random.uniform(0.5, 1.0)
}
analytics_data.append(analytics_entry)
return {"success": True, "message": "User stored successfully"}
@app.get("/process/users")
async def get_processed_users():
"""ユーザーデータの処理と分析"""
if not users_data:
return {"users": [], "summary": {}}
df = pd.DataFrame(users_data)
# データ処理
df['createdAt'] = pd.to_datetime(df['createdAt'])
df['days_since_creation'] = (datetime.now() - df['createdAt']).dt.days
# 統計情報の計算
summary = {
"total_users": len(df),
"active_users": len(df[df['days_since_creation'] <= 30]),
"avg_days_since_creation": df['days_since_creation'].mean(),
"role_distribution": df['role'].value_counts().to_dict()
}
return {
"users": df.to_dict('records'),
"summary": summary
}
@app.get("/analytics/summary")
async def get_analytics_summary():
"""分析サマリーの生成"""
if not analytics_data:
# デモデータの生成
demo_data = generate_demo_analytics_data()
return demo_data
df = pd.DataFrame(analytics_data)
df['date'] = pd.to_datetime(df['date'])
# 日別集計
daily_stats = df.groupby('date').agg({
'users': 'sum',
'revenue': 'sum'
}).reset_index()
# トレンドデータの準備
trends = []
for _, row in daily_stats.iterrows():
trends.append({
"date": row['date'].strftime('%Y-%m-%d'),
"users": int(row['users']),
"revenue": float(row['revenue'])
})
return {
"summary": {
"totalUsers": len(users_data),
"activeUsers": len([u for u in users_data if (datetime.now() - datetime.fromisoformat(u['createdAt'].replace('Z', '+00:00'))).days <= 30]),
"revenue": float(df['revenue'].sum())
},
"trends": trends
}
def generate_demo_analytics_data():
"""デモ用分析データの生成"""
start_date = datetime.now() - timedelta(days=30)
trends = []
for i in range(30):
date = start_date + timedelta(days=i)
trends.append({
"date": date.strftime('%Y-%m-%d'),
"users": int(np.random.uniform(10, 100)),
"revenue": float(np.random.uniform(1000, 10000))
})
return {
"summary": {
"totalUsers": int(np.random.uniform(500, 2000)),
"activeUsers": int(np.random.uniform(100, 500)),
"revenue": float(np.random.uniform(50000, 200000))
},
"trends": trends
}
@app.post("/analytics/query")
async def custom_analytics_query(query: AnalyticsQuery):
"""カスタム分析クエリの実行"""
try:
# クエリに基づくデータ処理
if query.metric == "user_growth":
return calculate_user_growth(query.start_date, query.end_date)
elif query.metric == "revenue_analysis":
return calculate_revenue_analysis(query.start_date, query.end_date)
else:
raise HTTPException(status_code=400, detail="Unsupported metric")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
def calculate_user_growth(start_date: str, end_date: str):
"""ユーザー成長率の計算"""
# 実装例
return {
"metric": "user_growth",
"period": f"{start_date} to {end_date}",
"growth_rate": 15.2,
"new_users": 127,
"churned_users": 23
}
def calculate_revenue_analysis(start_date: str, end_date: str):
"""売上分析の計算"""
# 実装例
return {
"metric": "revenue_analysis",
"period": f"{start_date} to {end_date}",
"total_revenue": 85000.0,
"avg_daily_revenue": 2833.33,
"revenue_sources": {
"subscriptions": 0.7,
"one_time": 0.3
}
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
統合とデプロイメント戦略
Docker Compose設定
docker-compose.yml
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:5000
volumes:
- ./frontend:/app
- /app/node_modules
depends_on:
- backend
backend:
build: ./backend
ports:
- "5000:5000"
environment:
- NODE_ENV=development
- PYTHON_API_URL=http://data-processor:8000
volumes:
- ./backend:/app
- /app/node_modules
depends_on:
- data-processor
data-processor:
build: ./data-processor
ports:
- "8000:8000"
environment:
- PYTHONPATH=/app
volumes:
- ./data-processor:/app
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- frontend
- backend
- data-processor
開発環境の起動
# 全サービスの起動
docker-compose up -d
# ログの確認
docker-compose logs -f
# 特定サービスの再起動
docker-compose restart backend
Cursorの活用テクニック
1. AI支援コード生成
プロンプトエンジニアリング
// AI: React コンポーネントでユーザー管理機能を作成
// 要件:
// - ユーザー一覧表示
// - CRUD操作対応
// - TypeScript使用
// - Material-UI風デザイン
2. コードレビューと最適化
Cursor の Cmd+L でAIチャットを開き:
このコンポーネントのパフォーマンスを改善してください。
特にレンダリング最適化とメモリ使用量に注目してください。
3. 自動テスト生成
// AI: この関数のJestテストを生成してください
// エッジケースとエラーハンドリングも含めて
export function validateUserData(user: User): ValidationResult {
// 実装...
}
4. ドキュメント自動生成
/**
* AI: この関数のJSDocを生成してください
* パラメータ、戻り値、使用例も含めて
*/
export async function processAnalyticsData(
data: AnalyticsData[],
options: ProcessingOptions
): Promise<ProcessedResult> {
// 実装...
}
トラブルシューティングとベストプラクティス
よくある問題と解決策
1. CORS エラー
// Node.js側
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:3000',
credentials: true
}));
2. TypeScript型定義エラー
// 共通型定義ファイルの作成
// types/common.ts
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
timestamp: string;
}
3. Python-Node.js 間の通信エラー
# Python側でのエラーハンドリング
@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={
"success": False,
"error": str(exc),
"timestamp": datetime.now().isoformat()
}
)
パフォーマンス最適化
フロントエンド最適化
// React.memo とカスタムフック
const OptimizedUserList = React.memo(({ users }: { users: User[] }) => {
const memoizedUsers = useMemo(
() => users.filter(user => user.active),
[users]
);
return (
<div>
{memoizedUsers.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
});
バックエンド最適化
// キャッシュとレート制限
import rateLimit from 'express-rate-limit';
import NodeCache from 'node-cache';
const cache = new NodeCache({ stdTTL: 600 }); // 10分キャッシュ
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分
max: 100 // リクエスト制限
});
app.use('/api/', limiter);
セキュリティ対策
// JWT認証の実装
import jwt from 'jsonwebtoken';
const authenticateToken = (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, process.env.JWT_SECRET!, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
};
まとめ
Cursorを使ったフルスタック開発では、以下のメリットを活用できます:
開発効率の向上
- AI支援によるコード生成とリファクタリング
- 自動補完によるタイピング時間の短縮
- インテリジェントなエラー検出と修正提案
コード品質の向上
- TypeScript統合による型安全性
- 自動テスト生成とカバレッジ向上
- ベストプラクティスの自動適用
学習とスキル向上
- AI からの実装提案による新しいパターン学習
- コードレビューによる改善点の発見
- 最新の開発トレンドの自動適用
現代的なフルスタック開発では、React、Node.js、Pythonの組み合わせが強力な選択肢となります。Cursorの AI支援機能を活用することで、これらの技術を効率的に学び、実践的なアプリケーションを迅速に開発することができます。
継続的な学習と実践により、より高度なアーキテクチャパターンやスケーラブルなシステム設計にも挑戦していきましょう。
関連記事