快速原型開發
上週我用Claude Code從零搭了一個全棧Todo應用,前後端+資料庫全都有。本來以為要搞兩三天,結果大半天就弄完了。
今天把這個過程寫下來,給你看看Claude Code到底能快到什麼程度。
專案概述
要做什麼
一個完整的Todo應用,包含:
- React前端(現代化介面)
- Express.js後端API
- MongoDB資料庫
- JWT使用者認證
- 完整的CRUD功能
- 可選的雲部署
技術棧
- 前端: React 18 + React Router + Axios
- 後端: Node.js + Express + Mongoose
- 資料庫: MongoDB
- 認證: JWT
節省了多少時間
| 開發方式 | 時間 |
|---|---|
| 傳統開發 | 16-24小時 |
| Claude Code | 2-3小時 |
大概快了8-10倍。
準備工作
需要什麼環境
先確保你的機器上有這些:
node --version # 需要16+
npm --version
mongod --version # 或者用MongoDB Atlas也行
建立專案
mkdir todo-app-fullstack
cd todo-app-fullstack
git init
git branch -M main
啟動Claude Code
claude
進入後會看到歡迎介面,就可以開始對話了。
開始構建
第一句話怎麼說
直接告訴Claude Code你要什麼:
我想做一个全栈Todo应用:
- React前端(Create React App)
- Express后端API
- MongoDB数据库
- JWT用户认证
- 完整的CRUD功能
帮我规划项目结构,创建基础文件。
Claude Code會自動:
- 建立前後端目錄結構
- 生成配置檔案
- 初始化package.json
- 建立基礎檔案
最終的專案結構
todo-app-fullstack/
├── backend/
│ ├── src/
│ │ ├── models/
│ │ ├── routes/
│ │ ├── middleware/
│ │ └── server.js
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── pages/
│ │ └── services/
│ └── package.json
└── README.md
後端開發
建立Express伺服器
先完成后端。创建Express服务器:
- CORS、body-parser等基础配置
- 错误处理中间件
- MongoDB连接
- 端口5000
Claude Code會生成完整的server.js:
backend/src/server.js:
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
// 中间件配置
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 数据库连接
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/todoapp', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('✓ MongoDB connected successfully'))
.catch(err => console.error('MongoDB connection error:', err));
// 路由
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', message: 'Server is running' });
});
// 路由导入(稍后添加)
const authRoutes = require('./routes/auth');
const todoRoutes = require('./routes/todos');
app.use('/api/auth', authRoutes);
app.use('/api/todos', todoRoutes);
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
success: false,
message: err.message || 'Something went wrong!',
});
});
// 404处理
app.use((req, res) => {
res.status(404).json({
success: false,
message: 'Route not found',
});
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`✓ Server is running on port ${PORT}`);
console.log(`✓ API available at http://localhost:${PORT}/api`);
});
module.exports = app;
建立資料模型
创建User和Todo的Mongoose模型:
User模型:username(唯一)、email(唯一)、password(哈希)、创建时间
Todo模型:title、description、completed、priority、user引用、时间戳
backend/src/models/User.js:
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: [true, 'Username is required'],
unique: true,
trim: true,
minlength: [3, 'Username must be at least 3 characters'],
maxlength: [30, 'Username cannot exceed 30 characters'],
},
email: {
type: String,
required: [true, 'Email is required'],
unique: true,
trim: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, 'Please provide a valid email'],
},
password: {
type: String,
required: [true, 'Password is required'],
minlength: [6, 'Password must be at least 6 characters'],
},
createdAt: {
type: Date,
default: Date.now,
},
});
// 密码哈希中间件
userSchema.pre('save', async function(next) {
// 只在密码被修改时才进行哈希
if (!this.isModified('password')) {
return next();
}
try {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (error) {
next(error);
}
});
// 密码验证方法
userSchema.methods.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password);
};
// 移除密码字段
userSchema.methods.toJSON = function() {
const user = this.toObject();
delete user.password;
return user;
};
module.exports = mongoose.model('User', userSchema);
backend/src/models/Todo.js:
const mongoose = require('mongoose');
const todoSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'Todo title is required'],
trim: true,
maxlength: [100, 'Title cannot exceed 100 characters'],
},
description: {
type: String,
trim: true,
maxlength: [500, 'Description cannot exceed 500 characters'],
},
completed: {
type: Boolean,
default: false,
},
priority: {
type: String,
enum: ['low', 'medium', 'high'],
default: 'medium',
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
updatedAt: {
type: Date,
default: Date.now,
},
});
// 更新时间戳
todoSchema.pre('save', function(next) {
this.updatedAt = Date.now();
next();
});
module.exports = mongoose.model('Todo', todoSchema);
實現認證系統
创建认证路由和中间件:
- 注册接口 POST /api/auth/register
- 登录接口 POST /api/auth/login
- JWT认证中间件
- 获取当前用户 GET /api/auth/me
backend/src/middleware/auth.js:
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const auth = async (req, res, next) => {
try {
// 从请求头获取token
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({
success: false,
message: 'No authentication token, access denied',
});
}
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key');
// 查找用户
const user = await User.findById(decoded.userId).select('-password');
if (!user) {
return res.status(401).json({
success: false,
message: 'User not found',
});
}
// 将用户信息添加到请求对象
req.user = user;
next();
} catch (error) {
res.status(401).json({
success: false,
message: 'Token is not valid',
});
}
};
module.exports = auth;
backend/src/routes/auth.js:
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const auth = require('../middleware/auth');
// 生成JWT Token
const generateToken = (userId) => {
return jwt.sign(
{ userId },
process.env.JWT_SECRET || 'your-secret-key',
{ expiresIn: '7d' }
);
};
// 注册
router.post('/register', async (req, res) => {
try {
const { username, email, password } = req.body;
// 验证必填字段
if (!username || !email || !password) {
return res.status(400).json({
success: false,
message: 'Please provide all required fields',
});
}
// 检查用户是否已存在
const existingUser = await User.findOne({
$or: [{ email }, { username }],
});
if (existingUser) {
return res.status(400).json({
success: false,
message: 'User with this email or username already exists',
});
}
// 创建新用户
const user = new User({ username, email, password });
await user.save();
// 生成token
const token = generateToken(user._id);
res.status(201).json({
success: true,
message: 'User registered successfully',
data: {
user: user.toJSON(),
token,
},
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
});
// 登录
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
// 验证必填字段
if (!email || !password) {
return res.status(400).json({
success: false,
message: 'Please provide email and password',
});
}
// 查找用户
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({
success: false,
message: 'Invalid credentials',
});
}
// 验证密码
const isPasswordValid = await user.comparePassword(password);
if (!isPasswordValid) {
return res.status(401).json({
success: false,
message: 'Invalid credentials',
});
}
// 生成token
const token = generateToken(user._id);
res.json({
success: true,
message: 'Login successful',
data: {
user: user.toJSON(),
token,
},
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
});
// 获取当前用户信息
router.get('/me', auth, async (req, res) => {
res.json({
success: true,
data: {
user: req.user,
},
});
});
module.exports = router;