基于 Turborepo + Next.js 14 + FastAPI 的全栈开发实践,适合中小型项目的架构设计和部署方案。

项目背景与定位
在多年的开发实践中,我发现自己积累的技术知识点往往零散分布在各个项目和学习笔记中。为了系统化梳理这些知识,我决定构建一个技术知识库平台。
核心需求:
- 统一管理学习笔记和技术文档
- 支持在线编辑和实时预览
- 提供优雅的文档展示体验
- 作为一个技术实践项目,探索全栈开发最佳实践
这个项目不仅满足了个人知识管理的需求,也希望通过开源分享,给其他开发者提供参考。
项目定位
这是一个中小型全栈项目,重点展示:
- Monorepo 架构在非超大规模项目中的应用
- Next.js 14 + FastAPI 的技术选型和开发实践
- Docker 容器化部署到生产环境的完整流程
- 技术知识库系统的设计与实现
适合场景:
- 个人技术博客和知识库
- 小团队的全栈项目架构参考
- 学习 Next.js 14 和 FastAPI 的实践案例
技术选型与架构设计
为什么选择 Monorepo?
在项目初期,我面临一个关键决策:是采用传统的多仓库(Multi-repo)架构,还是选择 Monorepo?
最终选择 Turborepo + Monorepo 架构,主要基于以下考量:
| 维度 | Multi-repo | Monorepo |
|---|---|---|
| 代码共享 | 需要发布 npm 包 | workspace 直接引用 |
| 版本管理 | 各仓库独立版本 | 统一版本控制 |
| CI/CD | 多个流水线 | 单一构建流程 |
| 依赖管理 | 容易版本冲突 | 统一依赖管理 |
| 跨包测试 | 困难 | 简单直接 |
核心技术栈
├── 前端框架层
│ ├── Next.js 14 (App Router) # React 全栈框架
│ ├── React 18 # UI 库
│ └── TypeScript 5 # 类型安全
│
├── 后端框架层
│ ├── FastAPI # Python 异步 Web 框架
│ ├── Python 3.10+ # 运行时
│ ├── Pydantic # 数据验证
│ ├── SQLAlchemy # ORM
│ ├── Alembic # 数据库迁移
│ └── Uvicorn # ASGI 服务器
│
├── 数据存储
│ ├── MySQL 8.0 # 关系型数据库
│ ├── Redis # 缓存与会话
│ └── Supabase # 云数据库 + 认证
│
├── 内容渲染
│ ├── next-mdx-remote # MDX/Markdown 渲染
│ └── @tailwindcss/typography # 文档样式插件
│
├── 构建工具
│ ├── Turborepo # Monorepo 构建工具
│ ├── pnpm # 高效的包管理器
│ └── Docker # 容器化部署
│
├── 样式方案
│ ├── Tailwind CSS # 原子化 CSS
│ └── styled-components # CSS-in-JS
│
├── 开发工具
│ ├── ESLint + Prettier # 代码规范
│ ├── Storybook # 组件开发
│ └── Changeset # 版本管理
│
└── 部署平台
├── Vercel # Serverless 前端部署
├── Docker + Linux # 后端容器化部署
└── Nginx # 反向代理
项目架构
┌─────────────────────────────────────────────────────────────┐
│ 用户访问层
│ web.erishen.cn (前端) │ admin.erishen.cn (后台)
│ (Interview Web) │ (Interview Admin)
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ API 网关层
│ api.erishen.cn (FastAPI)
│ - 商品管理 API (Items API)
│ - 认证 API (Auth API)
│ - 文档 API (Docs API)
│ - Redis API (缓存服务)
└─────────────────────────────────────────────────────────────┘
↓
┌──────────────────┴──────────────────┐
↓ ↓
┌──────────────────┐ ┌──────────────────┐
│ MySQL 8.0 │ │ Redis
│ 商品/文档数据 │ │ 缓存/会话
└──────────────────┘ └──────────────────┘
项目结构
┌─────────────────────────────────────────────────────────────┐
│ Interview (前端 Monorepo)
├─────────────────────────────────────────────────────────────┤
│ apps/
│ ├── web/ # 主应用 (端口 3000)
│ │ └── src/
│ │ ├── app/
│ │ │ ├── docs/ # 文档展示系统
│ │ │ │ ├── page.tsx # 文档列表
│ │ │ │ └── [slug]/page.tsx # 文档详情
│ │ │ ├── api-integration/ # API 集成演示
│ │ │ └── ...
│ │ └── lib/
│ │ └── docs.ts # 文档加载工具
│ └── admin/ # 管理后台 (端口 3003)
│
│ packages/
│ ├── ui/ # 共享 UI 组件库
│ ├── api-client/ # API 客户端
│ ├── utils/ # 工具函数
│ ├── types/ # 类型定义
│ ├── config/ # 配置文件
│ └── constants/ # 常量定义
│
│ docs/ # 知识库文档 (Markdown 源文件)
│ ├── README.md # 文档导航
│ ├── frontend.md # 前端基础知识
│ ├── frontend-extended.md # 前端扩展知识
│ ├── dynamic-programming.md # 动态规划
│ ├── case1.md # 综合题库
│ └── ...
│
│ scripts/ # 工具脚本
│ turbo.json # Turborepo 配置
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ fastapi-web (后端服务)
├─────────────────────────────────────────────────────────────┤
│ app/
│ ├── main.py # FastAPI 应用入口
│ ├── routers/ # API 路由层
│ │ ├── items.py # 商品 API
│ │ ├── auth.py # 认证 API
│ │ ├── docs.py # 文档 API
│ │ ├── redis.py # Redis API
│ │ └── system.py # 系统 API
│ ├── config.py # 配置管理
│ ├── crud.py # 数据库 CRUD 操作
│ ├── models.py # 数据模型层 (SQLAlchemy)
│ │ ├── Item # 商品模型
│ │ └── DocLog # 文档日志模型
│ ├── schemas.py # Pydantic 数据验证
│ ├── security.py # 安全工具(JWT、认证)
│ ├── security_headers.py # 安全响应头
│ ├── middleware.py # 中间件(日志、限流)
│ ├── ip_filter.py # IP 黑名单/白名单过滤
│ ├── path_protection.py # 敏感路径保护
│ ├── redis_client.py # Redis 客户端
│ ├── database.py # 数据库连接
│ ├── exceptions.py # 异常处理
│ └── factory.py # 应用工厂
│
│ alembic/ # 数据库迁移
│ tests/ # 测试用例
│ requirements.txt # Python 依赖
│ Dockerfile # 容器构建配置
│ docker-compose.yml # Docker Compose 配置
└─────────────────────────────────────────────────────────────┘
核心功能模块
1. Web 应用(前端知识库)
Web 应用是项目的主入口,提供完整的面试知识库浏览和搜索功能:
- 知识库导航:结构化的文档分类展示
- 文档渲染:基于
next-mdx-remote的 Markdown 实时渲染 - 自动加载:从 Admin API 或本地文件系统获取文档
- 代码高亮:优雅的代码块展示和语法高亮
- 搜索功能:基于关键词的快速检索
- 响应式设计:适配移动端和桌面端
文档展示系统实现
文档加载逻辑
文档内容通过 Admin API 获取,构建时回退到本地文件系统:
// src/lib/docs.ts
import fs from 'fs';
import path from 'path';
export interface Doc {
slug: string;
title: string;
description?: string;
}
const DOCS_DIR = path.join(process.cwd(), '../../docs');
const ADMIN_API_URL = process.env.NEXT_PUBLIC_ADMIN_URL || 'http://localhost:3003';
const DOCS_API_ENDPOINT = `${ADMIN_API_URL}/api/docs-public`;
// 从 Admin API 获取文档列表(生产环境)
async function fetchDocsFromAdmin(): Promise<Doc[]> {
try {
const response = await fetch(DOCS_API_ENDPOINT, {
cache: 'no-store',
headers: {
'Referer': process.env.NEXT_PUBLIC_WEB_URL || 'http://localhost:3000',
'Origin': process.env.NEXT_PUBLIC_WEB_URL || 'http://localhost:3000',
},
});
const data = await response.json();
return data.success ? data.docs : [];
} catch (error) {
console.error('[Docs API] Error fetching docs from Admin:', error);
return [];
}
}
// 从本地文件系统获取文档列表(构建时回退)
function getLocalDocs(): Doc[] {
if (!fs.existsSync(DOCS_DIR)) {
return [];
}
const files = fs.readdirSync(DOCS_DIR);
return files
.filter(file => file.endsWith('.md'))
.map(file => {
const slug = file.replace(/\.md$/, '');
const content = fs.readFileSync(path.join(DOCS_DIR, file), 'utf-8');
const titleMatch = content.match(/^#\s+(.+)$/m);
return {
slug,
title: titleMatch ? titleMatch[1] : slug,
description: content.match(/^> (.+)$/m)?.[1]
};
});
}
// 获取所有文档(生产环境优先 Admin API,构建时或开发环境优先本地)
export async function getAllDocs(): Promise<Doc[]> {
const isProduction = process.env.NODE_ENV === 'production';
const isBuildTime = process.env.NEXT_PHASE?.includes('build');
// 构建时或开发环境:优先本地文件
if (isBuildTime || !isProduction) {
const localDocs = getLocalDocs();
if (localDocs.length > 0) return localDocs;
return await fetchDocsFromAdmin();
}
// 生产环境:优先从 Admin API,失败则降级到本地文件
const adminDocs = await fetchDocsFromAdmin();
if (adminDocs.length > 0) return adminDocs;
return getLocalDocs();
}
// 获取单个文档
export async function getDocBySlug(slug: string): Promise<string | null> {
const isProduction = process.env.NODE_ENV === 'production';
const isBuildTime = process.env.NEXT_PHASE?.includes('build');
async function fetchDocFromAdmin(slug: string): Promise<string | null> {
try {
const url = new URL(DOCS_API_ENDPOINT);
url.searchParams.set('slug', slug);
const response = await fetch(url.toString(), { cache: 'no-store' });
const data = await response.json();
return data.success ? data.doc.content : null;
} catch (error) {
console.error(`[Docs API] Error fetching doc ${slug}:`, error);
return null;
}
}
function getLocalDocBySlug(slug: string): string | null {
const filePath = path.join(DOCS_DIR, `${slug}.md`);
return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : null;
}
// 构建时或开发环境:优先本地文件
if (isBuildTime || !isProduction) {
const localDoc = getLocalDocBySlug(slug);
if (localDoc) return localDoc;
return await fetchDocFromAdmin(slug);
}
// 生产环境:优先从 Admin API
const adminDoc = await fetchDocFromAdmin(slug);
if (adminDoc) return adminDoc;
return getLocalDocBySlug(slug);
}
文档列表页面
// src/app/docs/page.tsx
import { getAllDocs } from '@/lib/docs';
import Link from 'next/link';
export default async function DocsPage() {
const docs = await getAllDocs();
// 按分类展示
const coreDocs = docs.filter(doc => ['frontend', 'frontend-extended'].includes(doc.slug));
const algorithmDocs = docs.filter(doc =>
['dynamic-programming', 'min-path-sum-explained'].includes(doc.slug)
);
return (
<div className="container mx-auto">
<h1>📚 前端面试知识库</h1>
<section>
<h2>🎯 核心基础知识</h2>
{coreDocs.map(doc => (
<Link key={doc.slug} href={`/docs/${doc.slug}`}>
<Card title={doc.title} description={doc.description} />
</Link>
))}
</section>
<section>
<h2>🧮 算法与数据结构</h2>
{algorithmDocs.map(doc => (
<Link key={doc.slug} href={`/docs/${doc.slug}`}>
<Card title={doc.title} description={doc.description} />
</Link>
))}
</section>
</div>
);
}
文档详情页面
// src/app/docs/[slug]/page.tsx
import { getDocBySlug } from '@/lib/docs';
import { MDXRemote } from 'next-mdx-remote/rsc';
export default async function DocDetailPage({ params }: { params: { slug: string } }) {
const content = await getDocBySlug(params.slug);
if (!content) return <div>文档未找到</div>;
return (
<article className="prose prose-slate prose-lg max-w-none">
<MDXRemote source={content} />
</article>
);
}
2. Admin 应用(技术演示平台)
Admin 应用是一个企业级技术演示平台,用于展示和验证多种前端/后端技术:
- 文档编辑器:在线编辑
docs/目录下的 Markdown 文档- 实时文档列表展示
- 在线 Markdown 编辑
- 创建新文档
- 实时预览效果
- 通过 Admin API 向 Web 应用提供文档内容
- 双认证系统:NextAuth.js 和 Passport.js 两种认证方式对比演示
- 安全验证:CSRF 保护、Lusca 安全中间件
- 缓存演示:Redis 连接和缓存操作
- API 集成:FastAPI 服务代理和跨服务通信
- 管理后台模板:Dashboard UI 和统计数据展示
3. FastAPI 后端服务
FastAPI 后端提供完整的 RESTful API 服务:
- 商品管理 API:完整的 CRUD 操作
- 商品列表查询(分页、搜索)
- 商品详情获取
- 商品创建和更新(需管理员权限)
- 商品删除(需管理员权限)
- 认证 API:JWT 认证和用户管理
- 登录认证
- Token 验证
- 权限控制
- 文档 API:知识库文档服务
- 文档列表和详情
- 文档操作日志
- 编辑权限控制
- 实时预览
- Redis API:缓存操作演示
- 键值存储
- 缓存管理
- 系统 API:健康检查和系统信息
计划中的功能
以下功能已规划实现,当前处于开发阶段:
- 实时通信:WebSocket 支持(计划中)
- 健康度评分系统
- 在线状态推送
- 安全机制:
- JWT 令牌认证
- 速率限制(防止 DDoS)
- 可疑访问检测和告警
- CORS 跨域配置
- 性能优化:
- Redis 缓存层
- 数据库连接池
- 异步 I/O 处理
- Gzip 压缩响应
FastAPI 实现代码示例
路由定义(商品 API)
# app/routers/items.py
from fastapi import APIRouter, HTTPException, Depends, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from .. import crud, schemas
from ..database import get_db
from ..security import get_current_user, get_admin_user
router = APIRouter(
prefix="/items",
tags=["商品管理"],
responses={404: {"description": "商品未找到"}}
)
@router.get("/", response_model=List[schemas.Item])
def read_items(
skip: int = Query(0, ge=0, description="跳过的记录数"),
limit: int = Query(10, ge=1, le=100, description="返回的记录数"),
db: Session = Depends(get_db),
current_user: Optional[dict] = Depends(lambda: None) # 公开访问,无需认证
):
"""获取商品列表(公开访问)"""
items = crud.get_items(db, skip=skip, limit=limit)
return items
@router.get("/search", response_model=List[schemas.Item])
def search_items(
keyword: str = Query(..., min_length=1, description="搜索关键词"),
skip: int = Query(0, ge=0),
limit: int = Query(10, ge=1, le=100),
db: Session = Depends(get_db),
current_user: Optional[dict] = Depends(lambda: None) # 公开访问,无需认证
):
"""搜索商品(公开访问)"""
items = crud.search_items(db, keyword=keyword, skip=skip, limit=limit)
return items
@router.get("/{item_id}", response_model=schemas.Item)
def read_item(
item_id: int,
db: Session = Depends(get_db),
current_user: Optional[dict] = Depends(lambda: None) # 公开访问,无需认证
):
"""获取单个商品(公开访问)"""
db_item = crud.get_item(db, item_id=item_id)
if db_item is None:
raise HTTPException(status_code=404, detail="商品未找到")
return db_item
@router.post("/", response_model=schemas.Item, status_code=201)
def create_item(
item: schemas.ItemCreate,
db: Session = Depends(get_db),
admin_user: dict = Depends(get_admin_user) # 需要管理员权限
):
"""创建新商品"""
return crud.create_item(db=db, item=item)
@router.put("/{item_id}", response_model=schemas.Item)
def update_item(
item_id: int,
item: schemas.ItemUpdate,
db: Session = Depends(get_db),
admin_user: dict = Depends(get_admin_user) # 需要管理员权限
):
"""更新商品信息"""
db_item = crud.update_item(db=db, item_id=item_id, item=item)
if db_item is None:
raise HTTPException(status_code=404, detail="商品未找到")
return db_item
@router.delete("/{item_id}")
def delete_item(
item_id: int,
db: Session = Depends(get_db),
admin_user: dict = Depends(get_admin_user) # 需要管理员权限
):
"""删除商品"""
success = crud.delete_item(db=db, item_id=item_id)
if not success:
raise HTTPException(status_code=404, detail="商品未找到")
return {"message": "商品删除成功"}
Pydantic 数据验证
# app/schemas.py
from pydantic import BaseModel, Field
from typing import Union, Optional
from datetime import datetime
class ItemBase(BaseModel):
name: str = Field(..., min_length=1, max_length=100, description="商品名称")
price: float = Field(..., gt=0, description="商品价格,必须大于0")
is_offer: Union[bool, None] = Field(default=None, description="是否为特价商品")
description: Optional[str] = Field(None, max_length=1000, description="商品描述")
class ItemCreate(ItemBase):
pass
class ItemUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1, max_length=100)
price: Optional[float] = Field(None, gt=0)
is_offer: Optional[bool] = None
description: Optional[str] = Field(None, max_length=1000)
class Item(ItemBase):
id: int
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Config:
from_attributes = True # Pydantic v2 语法
3. 共享组件库(packages/ui)
在 Monorepo 架构中,共享组件库是提升开发效率的关键:
// 任意应用中导入共享组件
import { Button, Card, Input } from "@interview/ui";
// 带类型提示和自动补全
<Button variant="primary" size="large">
点击我
</Button>
知识库内容体系
文档结构
整个知识库文档按难度和领域进行分类,形成完整的学习路径:
docs/
├── README.md # 导航索引
├── frontend.md # 基础知识 (70KB)
├── frontend-extended.md # 扩展知识 (46KB)
├── dynamic-programming.md # 动态规划 (18KB)
├── min-path-sum-explained.md # 最小路径和详解
├── frontend-algorithms-practical.md # 实际工作算法
├── case1.md # 综合题库 (优化版)
├── styled-components-guide.md # styled-components 指南
├── REDIS_USAGE.md # Redis 使用指南
└── PACKAGES_VS_SHARED.md # 包与共享代码
最新优化内容
case1.md 综合题库优化亮点:
- 格式修正:修正编号错误,统一的文档结构
- 代码示例:每个问题都添加了详细的 TypeScript 代码
- 对比表格:多种技术方案的横向对比(如 LRU vs LFU、gRPC vs REST)
- 实战案例:第 8 题提供 3 个完整的 STAR 法则案例
- 完整实现:LRU/LFU 缓存的完整 TypeScript 实现
- 质量监控:WebSocket 健康度评分系统
学习路径推荐
初学者路径
前端基础知识 → 实际工作中的算法 → 动态规划入门
进阶开发者路径
前端扩展知识 → 最小路径和详解 → 深入特定技术栈
面试冲刺路径
核心知识点 → 算法基础 → 深度拓展 → 实战经验 → 综合题库
技术亮点与最佳实践
1. 前端:Turborepo 构建优化
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
优势:
- 智能缓存机制,避免重复构建
- 并行执行任务,提升构建速度
- 依赖关系自动分析
2. 后端:FastAPI 异步架构
# app/factory.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .routers import items, system, auth, redis, doc_logs
def create_app():
"""创建 FastAPI 应用实例"""
app = FastAPI(
title="FastAPI Web API",
description="全栈演示平台后端服务",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# CORS 配置
# 注意:allow_origins=["*"] 仅用于开发环境,生产环境请指定具体域名
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 生产环境请改为 ["https://web.erishen.cn", ...]
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 路由注册
app.include_router(items.router, prefix="/items", tags=["商品管理"])
app.include_router(auth.router, prefix="/auth", tags=["认证"])
app.include_router(docs.router, prefix="/docs", tags=["文档"])
app.include_router(redis.router, prefix="/redis", tags=["Redis"])
app.include_router(system.router, tags=["系统"])
return app
优势:
- 原生异步支持,高并发性能
- 自动生成 OpenAPI 文档(Swagger)
- 类型注解驱动的数据验证(Pydantic)
- 内置依赖注入系统
3. 后端:分层架构设计
app/
├── routers/ # API 路由层
│ ├── items.py # 商品 API
│ ├── auth.py # 认证 API
│ ├── docs.py # 文档 API
│ ├── redis.py # Redis API
│ └── system.py # 系统 API
├── config.py # 配置管理
├── crud.py # 数据库 CRUD 操作
├── models.py # 数据模型层 (SQLAlchemy)
│ ├── Item # 商品模型
│ └── DocLog # 文档日志模型
├── schemas.py # Pydantic 数据验证
├── security.py # 安全工具(JWT、认证)
├── security_headers.py # 安全响应头
├── middleware.py # 中间件(日志、限流)
├── ip_filter.py # IP 黑名单/白名单过滤
├── path_protection.py # 敏感路径保护
├── redis_client.py # Redis 客户端
├── database.py # 数据库连接
├── exceptions.py # 异常处理
└── factory.py # 应用工厂
优势:
- 职责分离,易于维护和测试
- 业务逻辑独立于 API 接口
- 可复用的服务层
- 灵活的中间件扩展
4. 安全机制:多层级防护
# app/middleware.py
from fastapi import Request, Response
from .security import is_suspicious_request
async def security_middleware(request: Request, call_next):
# 1. 可疑访问检测
if is_suspicious_request(request):
logger.warning(f"⚠️ 可疑访问检测: IP={request.client.host}")
return Response(status_code=403)
# 2. 速率限制
if await rate_limiter.is_exceeded(request):
logger.warning(f"🚫 速率限制: IP={request.client.host}")
return Response(
content="Too many requests",
status_code=429
)
# 3. 日志记录
logger.info(f"{request.method} {request.url.path}")
response = await call_next(request)
return response
防护措施:
- ✅ JWT 令牌认证
- ✅ 速率限制(防止滥用)
- ✅ 可疑访问检测
- ✅ CORS 跨域控制
- ✅ SQL 注入防护(ORM)
- ✅ XSS 防护(输入验证)
2. MDX/Markdown 渲染方案
使用 next-mdx-remote + @tailwindcss/typography 实现优雅的文档渲染:
// 安装依赖
pnpm add next-mdx-remote @tailwindcss/typography
// Tailwind 配置
// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/typography')],
};
// 使用 Tailwind Typography 类
<article className="prose prose-slate prose-lg max-w-none">
<MDXRemote source={content} />
</article>
支持的 Markdown 特性:
- ✅ 标题(H1-H6)
- ✅ 代码块和语法高亮
- ✅ 表格
- ✅ 引用块
- ✅ 列表(有序/无序)
- ✅ 粗体、斜体
- ✅ 链接和图片
- ✅ 分隔线
3. Workspace 协议
{
"dependencies": {
"@interview/ui": "workspace:*",
"@interview/utils": "workspace:*"
}
}
使用 workspace:* 协议可以实现:
- 开发时实时引用源码
- 构建时自动链接
- 版本统一管理
4. 环境变量管理
项目支持多环境配置:
# .env.example # 模板文件
# .env.local # 本地开发(不提交)
# .env.vercel # Vercel 部署
5. 组件开发流程
使用 Storybook 进行组件开发:
# 启动 Storybook
pnpm storybook
# 构建静态文档
pnpm build-storybook
优势:
- 组件独立开发
- 实时预览效果
- 文档自动生成
开发体验优化
1. 统一的代码规范
// .eslintrc.js
module.exports = {
extends: ["@interview/eslint-config"]
}
2. 类型安全保障
TypeScript 配置支持路径别名:
// tsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"],
"@interview/ui": ["./packages/ui/src"]
}
}
}
3. 实用工具脚本
# 清理端口占用
pnpm kill-ports
# 清理数据库连接
pnpm clean-connections
部署与运维
前端部署:Vercel
项目配置了 Vercel 自动部署:
// vercel.json
{
"buildCommand": "pnpm build",
"installCommand": "pnpm install"
}
部署环境:
- Web 应用:https://web.erishen.cn
- Admin 应用:https://admin.erishen.cn
部署特点:
- ✅ 自动化 CI/CD(Git 推送自动部署)
- ✅ Serverless 函数(按需付费)
- ✅ 全球 CDN 加速
- ✅ 自动 HTTPS 证书
后端部署:Docker + Linux
FastAPI 后端采用 Docker 容器化部署到 Linux 服务器:
# docker-compose.prod.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: fastapi-web-app
network_mode: host
restart: always
environment:
- APP_ENV=production
- PORT=8086
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
volumes:
- ./app:/app/app
- ./logs:/app/logs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8086/health"]
interval: 30s
retries: 3
部署环境:
- FastAPI 服务:https://api.erishen.cn
部署特点:
- ✅ Docker 容器化(环境一致性)
- ✅ 容器自动重启(
restart: always) - ✅ 健康检查(自动恢复)
- ✅ Nginx 反向代理
- ✅ SSL/TLS 加密通信
数据库设计
MySQL 关系型数据库
-- 商品表
CREATE TABLE items (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
price FLOAT NOT NULL,
is_offer INT DEFAULT 0, -- 0=False, 1=True
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 文档操作日志表
CREATE TABLE doc_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
action VARCHAR(50) NOT NULL, -- 操作类型: create/update/delete
doc_slug VARCHAR(100) NOT NULL,
user_id VARCHAR(100),
user_email VARCHAR(100),
user_name VARCHAR(100),
auth_method VARCHAR(50), -- 认证方式: nextauth/passport
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
details TEXT
);
-- 索引优化
CREATE INDEX idx_items_name ON items(name);
CREATE INDEX idx_doc_logs_slug ON doc_logs(doc_slug);
CREATE INDEX idx_doc_logs_user ON doc_logs(user_id);
Redis 缓存策略
# app/redis_client.py
from redis import Redis
from json import dumps, loads
class RedisClient:
def __init__(self):
self.redis = Redis(
host=os.getenv("REDIS_HOST", "localhost"),
port=int(os.getenv("REDIS_PORT", "6379")),
password=os.getenv("REDIS_PASSWORD"),
decode_responses=True
)
# 缓存商品列表(TTL 5 分钟)
async def cache_products(self, products: list):
await self.redis.setex(
"products:list",
300,
dumps(products)
)
# 缓存用户会话(TTL 30 分钟)
async def cache_session(self, user_id: int, session: dict):
await self.redis.setex(
f"session:{user_id}",
1800,
dumps(session)
)
# 缓存文档内容(TTL 10 分钟)
async def cache_doc(self, slug: str, content: str):
await self.redis.setex(
f"doc:{slug}",
600,
content
)
缓存策略:
- 商品列表:5 分钟(热点数据)
- 用户会话:30 分钟
- 文档内容:10 分钟
- 系统配置:1 小时
开发环境启动
# 安装依赖
pnpm install
# 启动所有应用
pnpm dev
# 启动单个应用
pnpm dev --filter=@interview/web
# 访问文档列表
open http://localhost:3000/docs
项目收益与反思
收益
- 全栈技术沉淀:系统梳理了前后端知识体系
- 工程化实践:掌握了 Monorepo + Docker 完整架构
- 学习效率:快速查找技术相关知识点
- 分享价值:帮助他人学习成长
- 文档系统:优雅的 Markdown 展示体验
- 实战经验:完成完整的产品开发流程
反思与改进
- 文档维护成本:内容更新需要持续投入
- 性能优化:大文档加载体验有待优化(可考虑分块加载)
- 社区参与:缺乏互动机制和用户反馈
- 搜索功能:可以增加全文搜索和智能推荐
- 后端监控:需要添加性能监控和告警系统
- 容器编排:未来可考虑 Kubernetes 集群部署
未来规划
前端优化
- [ ] 添加全文搜索功能(ElasticSearch)
- [ ] 支持文档评论和互动
- [ ] 实现深色模式
- [ ] 添加阅读进度和书签功能
- [ ] 支持导出 PDF
- [ ] 数据埋点,用户访问信息记录
后端优化
- [ ] WebSocket 实时通信功能(计划中)
- [ ] 健康度评分系统(计划中)
- [ ] 添加性能监控(Prometheus + Grafana)
- [ ] 实现分布式追踪(Jaeger)
- [ ] 添加消息队列(RabbitMQ/Celery)
- [ ] 优化数据库查询(读写分离)
- [ ] 实现 GraphQL API(计划中)
运维优化
- [ ] CI/CD 自动化流程优化
- [ ] 容器编排迁移到 Kubernetes
- [ ] 自动化测试覆盖率提升
- [ ] 灰度发布和回滚机制
项目局限性
为了保持文章的真实性,这里也说明一下当前项目的局限性:
- 项目规模较小:这不是一个超大规模的系统,API 数量和业务复杂度有限
- 缺少单元测试:当前项目还没有完整的单元测试覆盖
- 未实现所有计划功能:WebSocket、性能监控等功能仍在规划中
- 性能数据缺失:没有做过高并发压力测试和性能基准测试
- 团队协作场景未验证:主要是个人项目,多团队协作的场景未充分验证
这个案例更适合作为中小型项目的架构参考,而不是超大规模系统的解决方案。如果你的项目规模更大,需要考虑更复杂的架构设计。
总结
这个项目是我的一个全栈开发实践案例,通过构建技术知识库,我总结了以下几点经验:
- Monorepo 适合中小团队:代码共享、统一依赖,但在超大规模项目时要谨慎评估
- 技术选型要务实:Next.js 14 + FastAPI 对于中小型项目是高效的选择
- 工程化很重要:Turborepo、Docker、CI/CD 能显著提升开发效率和部署可靠性
- 从简单开始迭代:先实现核心功能,再逐步完善(如后续计划添加的 WebSocket)
- 安全意识要贯穿始终:即使小型项目,也要做好基础安全防护
项目已部署并稳定运行,包含了从开发到部署的完整流程。通过这个案例,我希望展示:
- 如何用现代化技术栈快速构建全栈应用
- 中小型项目的架构设计思路
- Docker 容器化部署的实际操作
这个项目还有很大的改进空间(如单元测试、性能监控、WebSocket 功能等),但作为一个可用系统的实现案例,希望能给其他开发者一些参考。如果你对项目有任何建议或想法,欢迎交流讨论!
项目地址
在线体验
- 🌐 Web 应用:https://web.erishen.cn
- 🛠️ 管理后台:https://admin.erishen.cn
- 📚 文档列表:https://web.erishen.cn/docs
- 🔌 API 服务:https://api.erishen.cn
- 📖 API 文档:https://api.erishen.cn/docs
技术栈
- 前端:Next.js 14 + React 18 + TypeScript 5 + Turborepo
- 后端:FastAPI + Python 3.10 + Pydantic v2 + SQLAlchemy
- 数据库:MySQL 8.0 + Redis
- 部署:Vercel + Docker + Nginx
发表回复