<< All versions
Skill v1.0.0
currentLLM-judged scan98/100virgo777/buddyme/backend-patterns
──Details
PublishedOctober 23, 2026 at 01:33 PM
Content Hashsha256:8966bc6c24a219e0...
Git SHA40fd6b4635ec
──Files
Files (1 file, 13.7 KB)
SKILL.md13.7 KBactive
SKILL.md · 600 lines · 13.7 KB
version: "1.0.0" name: backend-patterns description: 后端架构模式、API 设计、数据库优化以及针对 Node.js、Express 和 Next.js API 路由的服务端最佳实践。 origin: ECC
后端开发模式 (Backend Development Patterns)
用于构建可扩展服务端应用的后端架构模式与最佳实践。
何时激活 (When to Activate)
- 设计 REST 或 GraphQL API 端点时
- 实现仓储层(Repository)、服务层(Service)或控制层(Controller)时
- 优化数据库查询(N+1 问题、索引、连接池)时
- 添加缓存(Redis、内存缓存、HTTP 缓存头)时
- 设置后台作业(Background jobs)或异步处理时
- 为 API 构建错误处理与数据校验机制时
- 编写中间件(身份验证、日志记录、限流)时
API 设计模式
RESTful API 结构
typescript
// ✅ 基于资源的 URLGET /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
仓储模式 (Repository Pattern)
typescript
// 抽象数据访问逻辑interface MarketRepository {findAll(filters?: MarketFilters): Promise<Market[]>findById(id: string): Promise<Market | null>create(data: CreateMarketDto): Promise<Market>update(id: string, data: UpdateMarketDto): Promise<Market>delete(id: string): Promise<void>}class SupabaseMarketRepository implements MarketRepository {async findAll(filters?: MarketFilters): Promise<Market[]> {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 queryif (error) throw new Error(error.message)return data}// 其他方法...}
服务层模式 (Service Layer Pattern)
typescript
// 将业务逻辑与数据访问分离class MarketService {constructor(private marketRepo: MarketRepository) {}async searchMarkets(query: string, limit: number = 10): Promise<Market[]> {// 业务逻辑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 || 0const scoreB = results.find(r => r.id === b.id)?.score || 0return scoreA - scoreB})}private async vectorSearch(embedding: number[], limit: number) {// 向量搜索实现}}
中间件模式 (Middleware Pattern)
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: 'Unauthorized' })}try {const user = await verifyToken(token)req.user = userreturn handler(req, res)} catch (error) {return res.status(401).json({ error: 'Invalid token' })}}}// 使用示例export default withAuth(async (req, res) => {// Handler 可以访问 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('*')
防止 N+1 查询
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)})
事务模式 (Transaction Pattern)
typescript
async function createMarketWithPosition(marketData: CreateMarketDto,positionData: CreatePositionDto) {// 使用 Supabase 事务 (RPC 调用)const { data, error } = await supabase.rpc('create_market_with_position', {market_data: marketData,position_data: positionData})if (error) throw new Error('Transaction failed')return data}// Supabase 中的 SQL 函数CREATE OR REPLACE FUNCTION create_market_with_position(market_data jsonb,position_data jsonb)RETURNS jsonbLANGUAGE plpgsqlAS $$BEGIN-- 自动开始事务INSERT INTO markets VALUES (market_data);INSERT INTO positions VALUES (position_data);RETURN jsonb_build_object('success', true);EXCEPTIONWHEN OTHERS THEN-- 发生错误时自动回滚RETURN jsonb_build_object('success', false, 'error', SQLERRM);END;$$;
缓存策略
Redis 缓存层
typescript
class CachedMarketRepository implements MarketRepository {constructor(private baseRepo: MarketRepository,private redis: RedisClient) {}async findById(id: string): Promise<Market | null> {// 首先检查缓存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<void> {await this.redis.del(`market:${id}`)}}
旁路缓存模式 (Cache-Aside Pattern)
typescript
async function getMarketWithCache(id: string): Promise<Market> {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('Market not found')// 更新缓存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: 'Validation failed',details: error.errors}, { status: 400 })}// 记录未预料的错误console.error('Unexpected error:', error)return NextResponse.json({success: false,error: 'Internal server 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)}}
指数退避重试 (Retry with Exponential Backoff)
typescript
async function fetchWithRetry<T>(fn: () => Promise<T>,maxRetries = 3): Promise<T> {let lastError: Errorfor (let i = 0; i < maxRetries; i++) {try {return await fn()} catch (error) {lastError = error as Errorif (i < maxRetries - 1) {// 指数退避:1s, 2s, 4sconst delay = Math.pow(2, i) * 1000await new Promise(resolve => setTimeout(resolve, delay))}}}throw lastError!}// 使用示例const data = await fetchWithRetry(() => fetchFromAPI())
身份验证与授权
JWT 令牌校验
typescript
import jwt from 'jsonwebtoken'interface JWTPayload {userId: stringemail: stringrole: 'admin' | 'user'}export function verifyToken(token: string): JWTPayload {try {const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayloadreturn payload} catch (error) {throw new ApiError(401, 'Invalid token')}}export async function requireAuth(request: Request) {const token = request.headers.get('authorization')?.replace('Bearer ', '')if (!token) {throw new ApiError(401, 'Missing authorization token')}return verifyToken(token)}// 在 API 路由中使用export async function GET(request: Request) {const user = await requireAuth(request)const data = await getDataForUser(user.userId)return NextResponse.json({ success: true, data })}
基于角色的访问控制 (RBAC)
typescript
type Permission = 'read' | 'write' | 'delete' | 'admin'interface User {id: stringrole: 'admin' | 'moderator' | 'user'}const rolePermissions: Record<User['role'], Permission[]> = {admin: ['read', 'write', 'delete', 'admin'],moderator: ['read', 'write', 'delete'],user: ['read', 'write']}export function hasPermission(user: User, permission: Permission): boolean {return rolePermissions[user.role].includes(permission)}export function requirePermission(permission: Permission) {return (handler: (request: Request, user: User) => Promise<Response>) => {return async (request: Request) => {const user = await requireAuth(request)if (!hasPermission(user, permission)) {throw new ApiError(403, 'Insufficient permissions')}return handler(request, user)}}}// 使用示例 - 使用高阶函数包装 handlerexport const DELETE = requirePermission('delete')(async (request: Request, user: User) => {// Handler 接收到经过身份验证且拥有权限的用户return new Response('Deleted', { status: 200 })})
限流 (Rate Limiting)
简单的内存限流器
typescript
class RateLimiter {private requests = new Map<string, number[]>()async checkLimit(identifier: string,maxRequests: number,windowMs: number): Promise<boolean> {const now = Date.now()const requests = this.requests.get(identifier) || []// 移除窗口期之前的旧请求const recentRequests = requests.filter(time => now - time < windowMs)if (recentRequests.length >= maxRequests) {return false // 超过限流}// 添加当前请求recentRequests.push(now)this.requests.set(identifier, recentRequests)return true}}const limiter = new RateLimiter()export async function GET(request: Request) {const ip = request.headers.get('x-forwarded-for') || 'unknown'const allowed = await limiter.checkLimit(ip, 100, 60000) // 100 次/分钟if (!allowed) {return NextResponse.json({error: 'Rate limit exceeded'}, { status: 429 })}// 继续处理请求}
后台任务与队列 (Background Jobs & Queues)
简单队列模式
typescript
class JobQueue<T> {private queue: T[] = []private processing = falseasync add(job: T): Promise<void> {this.queue.push(job)if (!this.processing) {this.process()}}private async process(): Promise<void> {this.processing = truewhile (this.queue.length > 0) {const job = this.queue.shift()!try {await this.execute(job)} catch (error) {console.error('Job failed:', error)}}this.processing = false}private async execute(job: T): Promise<void> {// 任务执行逻辑}}// 市场索引任务示例interface IndexJob {marketId: string}const indexQueue = new JobQueue<IndexJob>()export async function POST(request: Request) {const { marketId } = await request.json()// 加入队列而非阻塞等待await indexQueue.add({ marketId })return NextResponse.json({ success: true, message: 'Job queued' })}
日志与监控 (Logging & Monitoring)
结构化日志
typescript
interface LogContext {userId?: stringrequestId?: stringmethod?: stringpath?: string[key: string]: unknown}class Logger {log(level: 'info' | 'warn' | 'error', message: string, context?: LogContext) {const entry = {timestamp: new Date().toISOString(),level,message,...context}console.log(JSON.stringify(entry))}info(message: string, context?: LogContext) {this.log('info', message, context)}warn(message: string, context?: LogContext) {this.log('warn', message, context)}error(message: string, error: Error, context?: LogContext) {this.log('error', message, {...context,error: error.message,stack: error.stack})}}const logger = new Logger()// 使用示例export async function GET(request: Request) {const requestId = crypto.randomUUID()logger.info('Fetching markets', {requestId,method: 'GET',path: '/api/markets'})try {const markets = await fetchMarkets()return NextResponse.json({ success: true, data: markets })} catch (error) {logger.error('Failed to fetch markets', error as Error, { requestId })return NextResponse.json({ error: 'Internal error' }, { status: 500 })}}
提示:后端模式旨在构建可扩展、可维护的服务端应用。请根据具体的复杂度需求选择合适的模式。