Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes.
适用于可扩展服务器端应用的后端架构模式和最佳实践。
typescript
// ✅ 基于资源的URL
GET /api/markets # 列出资源
GET /api/markets/:id # 获取单个资源
POST /api/markets # 创建资源
PUT /api/markets/:id # 替换资源
PATCH /api/markets/:id # 更新资源
DELETE /api/markets/:id # 删除资源
// ✅ 用于过滤、排序、分页的查询参数
GET /api/markets?status=active&sort=volume&limit=20&offset=0
typescript
// 抽象数据访问逻辑
interface MarketRepository {
findAll(filters?: MarketFilters): Promise
findById(id: string): Promise
create(data: CreateMarketDto): Promise
update(id: string, data: UpdateMarketDto): Promise
delete(id: string): Promise
}
class SupabaseMarketRepository implements MarketRepository {
async findAll(filters?: MarketFilters): Promise
let query = supabase.from(markets).select(*)
if (filters?.status) {
query = query.eq(status, filters.status)
}
if (filters?.limit) {
query = query.limit(filters.limit)
}
const { data, error } = await query
if (error) throw new Error(error.message)
return data
}
// 其他方法...
}
typescript
// 业务逻辑与数据访问分离
class MarketService {
constructor(private marketRepo: MarketRepository) {}
async searchMarkets(query: string, limit: number = 10): Promise
// 业务逻辑
const embedding = await generateEmbedding(query)
const results = await this.vectorSearch(embedding, limit)
// 获取完整数据
const markets = await this.marketRepo.findByIds(results.map(r => r.id))
// 按相似度排序
return markets.sort((a, b) => {
const scoreA = results.find(r => r.id === a.id)?.score || 0
const scoreB = results.find(r => r.id === b.id)?.score || 0
return scoreA - scoreB
})
}
private async vectorSearch(embedding: number[], limit: number) {
// 向量搜索实现
}
}
typescript
// 请求/响应处理管道
export function withAuth(handler: NextApiHandler): NextApiHandler {
return async (req, res) => {
const token = req.headers.authorization?.replace(Bearer , )
if (!token) {
return res.status(401).json({ error: 未授权 })
}
try {
const user = await verifyToken(token)
req.user = user
return handler(req, res)
} catch (error) {
return res.status(401).json({ error: 无效的令牌 })
}
}
}
// 使用方式
export default withAuth(async (req, res) => {
// 处理程序可以访问 req.user
})
typescript
// ✅ 良好实践:仅选择需要的列
const { data } = await supabase
.from(markets)
.select(id, name, status, volume)
.eq(status, active)
.order(volume, { ascending: false })
.limit(10)
// ❌ 不良实践:选择所有内容
const { data } = await supabase
.from(markets)
.select(*)
typescript
// ❌ 不良实践:N+1查询问题
const markets = await getMarkets()
for (const market of markets) {
market.creator = await getUser(market.creator_id) // N次查询
}
// ✅ 良好实践:批量获取
const markets = await getMarkets()
const creatorIds = markets.map(m => m.creator_id)
const creators = await getUsers(creatorIds) // 1次查询
const creatorMap = new Map(creators.map(c => [c.id, c]))
markets.forEach(market => {
market.creator = creatorMap.get(market.creator_id)
})
typescript
async function createMarketWithPosition(
marketData: CreateMarketDto,
positionData: CreatePositionDto
) {
// 使用Supabase事务
const { data, error } = await supabase.rpc(createmarketwith_position, {
market_data: marketData,
position_data: positionData
})
if (error) throw new Error(事务失败)
return data
}
// Supabase中的SQL函数
CREATE OR REPLACE FUNCTION createmarketwith_position(
market_data jsonb,
position_data jsonb
)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
BEGIN
-- 自动开始事务
INSERT INTO markets VALUES (market_data);
INSERT INTO positions VALUES (position_data);
RETURN jsonbbuildobject(success, true);
EXCEPTION
WHEN OTHERS THEN
-- 自动回滚
RETURN jsonbbuildobject(success, false, error, SQLERRM);
END;
$$;
typescript
class CachedMarketRepository implements MarketRepository {
constructor(
private baseRepo: MarketRepository,
private redis: RedisClient
) {}
async findById(id: string): Promise
// 先检查缓存
const cached = await this.redis.get(market:${id})
if (cached) {
return JSON.parse(cached)
}
// 缓存未命中 - 从数据库获取
const market = await this.baseRepo.findById(id)
if (market) {
// 缓存5分钟
await this.redis.setex(market:${id}, 300, JSON.stringify(market))
}
return market
}
async invalidateCache(id: string): Promise
await this.redis.del(market:${id})
}
}
typescript
async function getMarketWithCache(id: string): Promise
const cacheKey = market:${id}
// 尝试缓存
const cached = await redis.get(cacheKey)
if (cached) return JSON.parse(cached)
// 缓存未命中 - 从数据库获取
const market = await db.markets.findUnique({ where: { id } })
if (!market) throw new Error(市场未找到)
// 更新缓存
await redis.setex(cacheKey, 300, JSON.stringify(market))
return market
}
typescript
class ApiError extends Error {
constructor(
public statusCode: number,
public message: string,
public isOperational = true
) {
super(message)
Object.setPrototypeOf(this, ApiError.prototype)
}
}
export function errorHandler(error: unknown, req: Request): Response {
if (error instanceof ApiError) {
return NextResponse.json({
success: false,
error: error.message
}, { status: error.statusCode })
}
if (error instanceof z.ZodError) {
return NextResponse.json({
success: false,
error: 验证失败,
details: error.errors
}, { status: 400 })
}
// 记录意外错误
console.error(意外错误:, error)
return NextResponse.json({
success: false,
error: 内部服务器错误
}, { status: 500 })
}
// 使用方式
export async function GET(request: Request) {
try {
const data = await fetchData()
return NextResponse.json({ success: true, data })
} catch (error) {
return errorHandler(error, request)
}
}
typescript
async function fetchWithRetry
fn: () => Promise
maxRetries = 3
): Promise
let lastError: Error
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
lastError = error as Error
if (i < maxRetries - 1) {
// 指数退避:1秒、2秒、4秒
const delay = Math.pow(2, i) * 1000
await new Promise(resolve => setTimeout(resolve, delay))
}
}
}
throw lastError!
}
// 使用方式
const data = await fetchWithRetry(() => fetchFromAPI
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 backend-patterns-1776373867 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 backend-patterns-1776373867 技能
skillhub install backend-patterns-1776373867
文件大小: 4.93 KB | 发布时间: 2026-4-17 15:01