ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Agentic Awesome Skills 后端开发模式指南:从 RESTful API 设计到可观测性治理的完整实战手册

Agentic Awesome Skills 后端开发模式指南:从 RESTful API 设计到可观测性治理的完整实战手册 AI 技能AI 插件【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,445 agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址https://gitcode.com/gh_mirrors/an/agentic-awesome-skills点击查看免费下载本指南以 AASAgentic Awesome Skills仓库中cc-skill-backend-patterns技能的详细参考文档为核心系统梳理 Node.js / Express / Next.js API Route 场景下可扩展后端应用的架构模式API 设计、数据库访问、缓存、错误处理、认证授权、限流、后台队列与结构化日志。读完本文你将能对照每个模式的可运行代码示例在真实项目中落地分层架构并通过仓库源码与目录索引定位技能的完整上下文做到既能照抄实现也能理解原理。该技能在仓库中被标记为risk: critical高风险场景需谨慎启用、source: community定位为面向可扩展服务端应用的元技能meta category并已在 catalog.json 与 skills_index.json 中登记索引支持 Codex 与 Claude 两类目标运行时。其根文件 SKILL.md 定义了激活条件与安全约束本文继承的详细过程来自 详细指南。一、技能定位何时启用 cc-skill-backend-patterns在仓库的技能编排中每个技能通过SKILL.md的 YAML frontmatter 声明触发条件。cc-skill-backend-patterns的描述为 Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes对应 catalog.json 中的触发词triggers包括api、database、optimization、server、node、js、express等。因此适用场景非常明确需要设计或重构 RESTful API 的 URL 结构与资源分层需要抽取 Repository数据访问与 Service业务逻辑层让 Next.js API Route 保持轻薄需要优化 Supabase / PostgreSQL 查询、消除 N1、引入事务需要引入 Redis 缓存层、错误处理中间件、JWT 认证与 RBAC 权限需要限流、后台任务队列与结构化日志治理。而它的Limitations根文档中明示同样重要仅在任务明确匹配上述范围时使用输出不能替代环境级验证、测试或专家评审当缺少必要输入、权限、安全边界或成功标准时应停下并请求澄清。这意味着本文所有代码都应在你的具体技术栈中重新验证后再上线。二、API 设计模式让路由、查询与分层可维护2.1 RESTful 资源化 URL 结构后端可扩展的第一步是让 API 的名词与动词分离。文档给出以资源为中心的 URL 设计// ✅ Resource-based URLs GET /api/markets # List resources GET /api/markets/:id # Get single resource POST /api/markets # Create resource PUT /api/markets/:id # Replace resource PATCH /api/markets/:id # Update resource DELETE /api/markets/:id # Delete resource // ✅ Query parameters for filtering, sorting, pagination GET /api/markets?statusactivesortvolumelimit20offset0要点解读集合名使用复数名词markets避免在 URL 中暴露动作动词如/getMarketsPUT表示整体替换PATCH表示局部更新语义不可混用过滤status、排序sort、分页limit/offset统一走查询参数保持资源标识纯净分页参数与返回结构应保持稳定便于前端与缓存层复用。2.2 Repository Pattern抽象数据访问Repository 层把怎么查与业务怎么用解耦使上层可以自由切换 Supabase、Prisma 或内存实现也便于单元测试注入 Mock// Abstract data access logic interface MarketRepository { findAll(filters?: MarketFilters): PromiseMarket[] findById(id: string): PromiseMarket | null create(data: CreateMarketDto): PromiseMarket update(id: string, data: UpdateMarketDto): PromiseMarket delete(id: string): Promisevoid } class SupabaseMarketRepository implements MarketRepository { async findAll(filters?: MarketFilters): PromiseMarket[] { 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 } // Other methods... }从源码结构看该实现体现了三个通用原则接口驱动调用方只依赖MarketRepository接口、链式条件构建按需叠加eq/limit避免为每个组合写死查询、错误显式抛出error不为空立即抛异常由上层错误处理器统一兜底。2.3 Service Layer Pattern业务逻辑与数据访问分离Service 层承载领域动作例如把向量检索 全量数据回填 相似度排序编排为一个可复用的业务方法// Business logic separated from data access class MarketService { constructor(private marketRepo: MarketRepository) {} async searchMarkets(query: string, limit: number 10): PromiseMarket[] { // Business logic const embedding await generateEmbedding(query) const results await this.vectorSearch(embedding, limit) // Fetch full data const markets await this.marketRepo.findByIds(results.map(r r.id)) // Sort by similarity 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) { // Vector search implementation } }关键设计取舍构造函数注入MarketRepositoryService 不直接依赖 Supabase/Redis 等具体客户端便于替换与测试default parameterlimit 10让调用方安全省略参数相似度分数从检索结果取出、按分数排序属于典型的检索-回填-重排三段式业务逻辑。2.4 Middleware Pattern请求/响应处理管道中间件把认证、日志、校验等横切关注点从业务处理器中剥离。文档以 Next.js API Route 的withAuth高阶函数为例// Request/response processing pipeline 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 user return handler(req, res) } catch (error) { return res.status(401).json({ error: Invalid token }) } } } // Usage export default withAuth(async (req, res) { // Handler has access to req.user })其本质是包装器wrapper外层函数接收处理器返回带前置逻辑的新处理器。req.user通过类型扩展挂载到请求对象上使下游处理器免于重复解析 token。多个中间件可以嵌套组合如withAuth(withRateLimit(handler))形成清晰的管道顺序。三、数据库模式查询优化、N1 治理与事务3.1 查询优化只取所需列文档用 Supabase 客户端对比了两种写法// ✅ GOOD: Select only needed columns const { data } await supabase .from(markets) .select(id, name, status, volume) .eq(status, active) .order(volume, { ascending: false }) .limit(10) // ❌ BAD: Select everything const { data } await supabase .from(markets) .select(*)显式列选择的价值不只是少传一点数据它缩小了数据库需要物化的行宽减少网络传输与内存占用并为数据库索引覆盖index-only scan创造条件。排序order字段若建立了索引volume DESC才能高效执行limit则配合分页避免全表结果集返回。3.2 N1 查询预防批量取回再内存关联N1 是服务端最常见的性能陷阱先取 N 条主记录再逐条查询关联数据产生 N1 次往返。文档给出前后对比// ❌ BAD: N1 query problem const markets await getMarkets() for (const market of markets) { market.creator await getUser(market.creator_id) // N queries } // ✅ GOOD: Batch fetch const markets await getMarkets() const creatorIds markets.map(m m.creator_id) const creators await getUsers(creatorIds) // 1 query const creatorMap new Map(creators.map(c [c.id, c])) markets.forEach(market { market.creator creatorMap.get(market.creator_id) })优化后的核心是IN 查询 Map 关联先收集所有creator_id一次查出全部创建者再用Map建立id → 对象的索引遍历主记录时以 O(1) 完成关联。数据库往返从1 N降为2且 Map 查找避免了嵌套循环带来的 O(N²) 复杂度。3.3 Transaction Pattern用 RPC 封装多表写入当一次业务操作涉及多张表如创建市场 创建持仓时必须保证原子性。文档给出的方案是 Supabase RPC PL/pgSQL 函数让数据库在服务端完成事务async function createMarketWithPosition( marketData: CreateMarketDto, positionData: CreatePositionDto ) { // Use Supabase transaction 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 } // SQL function in Supabase CREATE OR REPLACE FUNCTION create_market_with_position( market_data jsonb, position_data jsonb ) RETURNS jsonb LANGUAGE plpgsql AS $$ BEGIN -- Start transaction automatically INSERT INTO markets VALUES (market_data); INSERT INTO positions VALUES (position_data); RETURN jsonb_build_object(success, true); EXCEPTION WHEN OTHERS THEN -- Rollback happens automatically RETURN jsonb_build_object(success, false, error, SQLERRM); END; $$;要点PL/pgSQL 函数体在单个事务中执行EXCEPTION WHEN OTHERS捕获任何错误并自动回滚SQLERRM返回错误信息客户端只需一次rpc调用既减少网络往返又把事务边界收拢在数据库层避免分布式事务的复杂度。注意这里INSERT INTO markets VALUES (market_data)是示意性写法实际应显式列出列名并做jsonb字段映射与校验。四、缓存策略Redis 缓存层与 Cache-Aside4.1 用装饰器式 Repository 叠加缓存缓存不应散落在业务代码里而应作为 Repository 的一个包装实现与原实现实现同一接口class CachedMarketRepository implements MarketRepository { constructor( private baseRepo: MarketRepository, private redis: RedisClient ) {} async findById(id: string): PromiseMarket | null { // Check cache first const cached await this.redis.get(market:${id}) if (cached) { return JSON.parse(cached) } // Cache miss - fetch from database const market await this.baseRepo.findById(id) if (market) { // Cache for 5 minutes await this.redis.setex(market:${id}, 300, JSON.stringify(market)) } return market } async invalidateCache(id: string): Promisevoid { await this.redis.del(market:${id}) } }该模式与 Repository 接口天然契合调用方无感知地获得缓存能力测试时也可单独验证baseRepo与redis的交互。setex设置了 300 秒5 分钟TTL避免缓存永久滞留invalidateCache用于写操作后的显式失效这是保证最终一致性的关键配套方法。4.2 Cache-Aside 读写流程Cache-Aside旁路缓存是最通用的缓存策略文档将其拆解为可复制的函数async function getMarketWithCache(id: string): PromiseMarket { const cacheKey market:${id} // Try cache const cached await redis.get(cacheKey) if (cached) return JSON.parse(cached) // Cache miss - fetch from DB const market await db.markets.findUnique({ where: { id } }) if (!market) throw new Error(Market not found) // Update cache await redis.setex(cacheKey, 300, JSON.stringify(market)) return market }完整闭环是读 → 命中缓存直接返回未命中 → 查库 → 回填缓存带 TTL→ 返回写 → 更新数据库 → 失效或更新缓存。文档使用TTL 过期而非永久缓存能自然消化数据变更高并发下如需更强一致性与防击穿可以在此基础上补充分布式锁或 singleflight但文档保持实现的最小可用性贴合选择与复杂度匹配的模式的收尾原则。五、错误处理模式集中式处理器与指数退避重试5.1 集中式错误处理器把业务异常、校验异常、未知异常三类错误统一收敛避免每个路由各自拼 JSONclass 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 }) } // Log unexpected errors console.error(Unexpected error:, error) return NextResponse.json({ success: false, error: Internal server error }, { status: 500 }) } // Usage export async function GET(request: Request) { try { const data await fetchData() return NextResponse.json({ success: true, data }) } catch (error) { return errorHandler(error, request) } }设计要点ApiError携带statusCode与isOperational是否为可预期的业务错误Object.setPrototypeOf修正继承链保证instanceof在 TS 目标下正确工作z.ZodError单独分支返回 400 与字段级details把校验错误暴露给前端定位未知异常统一记日志并返回泛化的 500不向客户端泄漏堆栈与内部细节——这是安全边界的一部分。5.2 指数退避重试对瞬时故障网络抖动、上游 5xx执行自动重试退避间隔随次数指数增长async function fetchWithRetryT( fn: () PromiseT, maxRetries 3 ): PromiseT { let lastError: Error for (let i 0; i maxRetries; i) { try { return await fn() } catch (error) { lastError error as Error if (i maxRetries - 1) { // Exponential backoff: 1s, 2s, 4s const delay Math.pow(2, i) * 1000 await new Promise(resolve setTimeout(resolve, delay)) } } } throw lastError! } // Usage const data await fetchWithRetry(() fetchFromAPI())重试序列为 1s、2s、4s2^i × 1000ms默认最多 3 次最后一次失败直接抛出lastError由上层错误处理器兜底。注意重试仅适用于幂等操作读、幂等写对非幂等写入应配合幂等键否则可能造成重复副作用。六、认证与授权JWT 校验与 RBAC 权限矩阵6.1 JWT Token 校验与请求鉴权import jwt from jsonwebtoken interface JWTPayload { userId: string email: string role: admin | user } export function verifyToken(token: string): JWTPayload { try { const payload jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload return 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) } // Usage in API route export async function GET(request: Request) { const user await requireAuth(request) const data await getDataForUser(user.userId) return NextResponse.json({ success: true, data }) }实现要点从Authorization: Bearer token提取 token缺失返回 401jwt.verify依赖process.env.JWT_SECRET校验签名与过期时间解码出的userId/role进入后续业务。安全提醒JWT_SECRET必须通过环境变量注入、绝不硬编码进源码且生产环境应使用强随机密钥。6.2 基于角色的访问控制RBACRBAC 用角色 → 权限矩阵集中声明权限边界type Permission read | write | delete | admin interface User { id: string role: admin | moderator | user } const rolePermissions: RecordUser[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 async (request: Request) { const user await requireAuth(request) if (!hasPermission(user, permission)) { throw new ApiError(403, Insufficient permissions) } return user } } // Usage export const DELETE requirePermission(delete)(async (request: Request) { // Handler with permission check })两个值得注意的设计requirePermission返回高阶函数可直接包裹处理器requirePermission(delete)(handler)与前面的中间件风格一脉相承授权失败抛 403区别于认证失败的 401语义准确权限矩阵集中在一个Record中新增角色只需改一处。七、限流内存滑动窗口限流器对公开 API 实施限流是防滥用与保护后端资源的基础手段。文档提供无外部依赖的内存实现class RateLimiter { private requests new Mapstring, number[]() async checkLimit( identifier: string, maxRequests: number, windowMs: number ): Promiseboolean { const now Date.now() const requests this.requests.get(identifier) || [] // Remove old requests outside window const recentRequests requests.filter(time now - time windowMs) if (recentRequests.length maxRequests) { return false // Rate limit exceeded } // Add current request 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 req/min if (!allowed) { return NextResponse.json({ error: Rate limit exceeded }, { status: 429 }) } // Continue with request }原理以identifier这里是 IP为键在Map中维护时间戳数组每次请求先剔除窗口外的旧时间戳再判断窗口内请求数是否达到maxRequests超限返回 429Too Many Requests。示例中配置为100 次/分钟。两点提示内存实现适合单实例与开发环境多实例部署时应替换为 Redis 等共享存储且真实多实例场景下x-forwarded-for需要可信代理头校验防止伪造绕过。八、后台任务与队列内存队列削峰耗时操作如向量索引、邮件发送不应阻塞请求线程。文档给出最小可用队列class JobQueueT { private queue: T[] [] private processing false async add(job: T): Promisevoid { this.queue.push(job) if (!this.processing) { this.process() } } private async process(): Promisevoid { this.processing true while (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): Promisevoid { // Job execution logic } } // Usage for indexing markets interface IndexJob { marketId: string } const indexQueue new JobQueueIndexJob() export async function POST(request: Request) { const { marketId } await request.json() // Add to queue instead of blocking await indexQueue.add({ marketId }) return NextResponse.json({ success: true, message: Job queued }) }要点add入队后立即返回API 只确认任务已排队200/202 语义不等待执行完成实现削峰单个processing标志保证同一时刻只有一个消费者循环避免并发重复消费单任务失败被捕获并记录不会中断整个队列。同时必须指出该实现的边界内存队列在进程重启后任务丢失、无持久化与重试语义、单实例串行执行。生产环境应替换为 BullMQ / Redis Streams / SQS 等具备持久化、重试、水平扩展能力的队列——这与文档结尾选择与复杂度匹配的模式的原则一致。九、日志与监控结构化日志贯穿请求链路结构化日志JSON 行是可观测性的地基。文档示例以requestId贯穿请求生命周期interface LogContext { userId?: string requestId?: string method?: string path?: 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() // Usage 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 }) } }可观测性要点每条日志输出为单行 JSON含timestamp、level、message、业务上下文可直接被 ELK / Loki / CloudWatch 等采集解析无需正则拆文本requestId由crypto.randomUUID()生成在请求入口创建、沿日志上下文传递使一次请求的所有日志可按 ID 串联是分布式排查的基础错误日志携带error.message与stack但响应仍只返回泛化的 500保证内部细节不外泄。十、落地清单如何把模式组合成可扩展后端基于本技能的全部模式一套推荐的组合落地顺序如下分层Route薄处理器→ Service业务编排→ Repository数据访问横切面用中间件数据访问Repository 内显式列选择 批量关联消除 N1 多表写走 RPC 事务缓存以CachedMarketRepository包装原 RepositoryCache-Aside TTL 写后失效安全JWT 校验中间件 RBAC 权限矩阵 集中式错误处理401/403/400/500 语义分层稳定性幂等场景指数退避重试 内存限流单实例或 Redis 限流多实例 后台队列削峰可观测性结构化 JSON 日志 requestId贯穿 错误堆栈留档。每个模式都应遵守根文档的安全约束输出不替代环境级验证启用前确认输入、权限、安全边界与成功标准齐全当复杂度上升多实例、高并发、强一致应升级对应的基础设施如 Redis 缓存与限流、持久化队列而不是在内存实现上打补丁。十一、在本仓库中继续深入技能的激活入口与安全约束见 SKILL.md详细过程即本文依据的 detailed-guide.md技能在 AAS 目录中的登记信息描述、category、risk、source、triggers见 catalog.json运行时支持矩阵Codex / Claude 均 supportedsetup type 为 none见 skills_index.json仓库还收录了同类主题技能可供对照如 nodejs-backend-patterns、dotnet-backend-patterns以及配套的 cc-skill-clickhouse-ioClickHouse 分析型负载优化与 cc-skill-coding-standards编码规范约束可交叉阅读形成完整的后端技能矩阵。结语正如指南结尾所强调——后端模式成就可扩展、可维护的服务端应用请选择与你的复杂度相匹配的模式。 本文继承的八组模式API 设计、仓库/服务层、查询优化、缓存、错误处理、认证授权、限流、队列与日志共同构成一套可逐步落地的后端工程化骨架在 AAS 仓库中它们以技能 详细指南的形式被打包为可检索、可复用的 Agent 技能资产供 Codex 与 Claude 在真实项目中按需加载执行。赞分享AI 技能AI 插件【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,445 agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址https://gitcode.com/gh_mirrors/an/agentic-awesome-skills点击查看免费下载相关推荐Agentic Awesome Skills 中的 Azure Monitor OpenTelemetry Python 可观测性 Skill 实战指南Agentic Awesome Skills 中的 Azure Monitor OpenTelemetry Python 可观测性 Skill 实战指南 本指南AI 技能AI 插件Agentic Awesome Skills 前端模式实战指南从组件组合到性能优化的 React 开发范式Agentic Awesome Skills 前端模式实战指南从组件组合到性能优化的 React 开发范式 本文以 AASAgentic Awesome SAI 技能AI 插件agentic-awesome-skills 之 API 安全最佳实践从令牌契约到可观测响应的完整防线agentic awesome skills 之 API 安全最佳实践从令牌契约到可观测响应的完整防线 导读 本文以 agentic awesome skilAI 技能AI 插件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表