Communitygithub.com

後端開發模式

後端架構模式、API 設計、資料庫優化以及 Node.js、Express 和 Next.js API 路由的伺服器端最佳實踐。在建立或審查 Node.js、Express 或 Next.js API 路由及其資料存取時使用。

後端開發模式 是什麼?

此「後端開發模式」技能提供了一套完整的後端架構最佳實踐指南,專為使用 Node.js、Express 或 Next.js API 路由的開發者所設計。技能涵蓋了從 API 設計(RESTful 結構、Repository 模式、Service 層、Middleware 模式)、資料庫優化(查詢優化、N+1 問題預防、交易模式)、快取策略(Redis 快取層、Cache-Aside 模式)、錯誤處理(集中式錯誤處理、指數退避重試)、認證授權(JWT 驗證、角色權限控制)到背景任務(簡單佇列模式)與監控(結構化日誌)等關鍵領域。每個模式均提供清晰易懂的 TypeScript 範例程式碼,並指出常見的錯誤寫法(FAIL)與推薦做法(PASS),幫助開發者建立可擴展、可維護且高效的後端服務。此技能適用於任何基於上述技術棧的專案,無論是在設計新系統或審查現有程式碼時,都能提供具體且可立即應用的指導方針。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/backend-patterns

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

後端開發模式 是做什麼的?

適用於可擴展伺服器端應用程式的後端架構模式與最佳實踐。

何時啟用

  • 設計 REST 或 GraphQL API 端點
  • 實作 Repository、Service 或 Controller 層
  • 最佳化資料庫查詢(N+1、索引、連線池)
  • 新增快取(Redis、記憶體、HTTP 快取標頭)
  • 設定背景工作或非同步處理
  • 為 API 建立錯誤處理與驗證結構
  • 建構中介軟體(身分驗證、日誌記錄、速率限制)

API 設計模式

RESTful API 結構

// PASS: 基於資源的 URL
GET    /api/markets                 # 列出資源
GET    /api/markets/:id             # 取得單一資源
POST   /api/markets                 # 建立資源
PUT    /api/markets/:id             # 取代資源
PATCH  /api/markets/:id             # 更新資源
DELETE /api/markets/:id             # 刪除資源

// PASS: 使用查詢參數進行過濾、排序、分頁
GET /api/markets?status=active&sort=volume&limit=20&offset=0

Repository 模式

// 抽象化資料存取邏輯
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 query

    if (error) throw new Error(error.message)
    return data
  }

  // 其他方法...
}

Service 層模式

// 業務邏輯與資料存取分離
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 || 0
      const scoreB = results.find(r => r.id === b.id)?.score || 0
      return scoreA - scoreB
    })
  }

  private async vectorSearch(embedding: number[], limit: number) {
    // 向量搜尋實作
  }
}

Middleware 模式

// 請求/回應處理管道
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) => {
  // Handler 可以存取 req.user
})

資料庫模式

查詢最佳化

// PASS: 良好:只選取需要的欄位
const { data } = await supabase
  .from('markets')
  .select('id, name, status, volume')
  .eq('status', 'active')
  .order('volume', { ascending: false })
  .limit(10)

// FAIL: 不良:選取所有欄位
const { data } = await supabase
  .from('markets')
  .select('*')

N+1 查詢預防

// FAIL: 不良:N+1 查詢問題
const markets = await getMarkets()
for (const market of markets) {
  market.creator = await getUser(market.creator_id)  // N 次查詢
}

// PASS: 良好:批次取得
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)
})

交易模式

async function createMarketWithPosition(
  marketData: CreateMarketDto,
  positionData: CreatePositionDto
) {
  // 使用 Supabase 交易
  const { data, error } = await supabase.rpc('create_market_with_position', {
    market_data: marketData,
    position_data: positionData
  })

  if (error) throw new Error('交易失敗')
  return data
}

// Supabase 中的 SQL 函數
CREATE OR REPLACE FUNCTION create_market_with_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 jsonb_build_object('success', true);
EXCEPTION
  WHEN OTHERS THEN
    -- 自動回滾
    RETURN jsonb_build_object('success', false, 'error', SQLERRM);
END;
$$;

快取策略

Redis 快取層

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 模式

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('未找到市場')

  // 更新快取
  await redis.setex(cacheKey, 300, JSON.stringify(market))

  return market
}

錯誤處理模式

集中式錯誤處理

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.issues
    }, { 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)
  }
}

指數退避重試

async function fetchWithRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  let lastError: Error

  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn()
    } catch (error) {
      lastError = error as Error

      if (i < maxRetries - 1) {
        // 指數退避:1s, 2s, 4s
        const delay = Math.pow(2, i) * 1000
        await new Promise(resolve => setTimeout(resolve, delay))
      }
    }
  }

  throw lastError!
}

// 用法
const data = await fetchWithRetry(() => fetchFromAPI())

身分驗證與授權

JWT 權杖驗證

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, '無效的權杖')
  }
}

export async function requireAuth(request: Request) {
  const token = request.headers.get('authorization')?.replace('Bearer ', '')

  if (!token) {
    throw new ApiError(401, '缺少授權權杖')
  }

  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 })
}

基於角色的存取控制

type Permission = 'read' | 'write' | 'delete' | 'admin'

interface User {
  id: string
  role: '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, '權限不足')
      }

      return handler(request, user)
    }
  }
}

// 用法 - HOF 包裝 handler
export const DELETE = requirePermission('delete')(
  async (request: Request, user: User) => {
    // Handler 接收已驗證且擁有權限的使用者
    return new Response('已刪除', { status: 200 })
  }
)

速率限制

速率限制必須使用共用儲存,例如 Redis、閘道器或平台原生的限制器。請勿對生產環境的 API 使用每個進程的記憶體計數器:它們會在部署時重置、在副本之間分裂,並且在無伺服器或多實例環境中會開放失效。

讓後端層負責選擇整合點和錯誤形狀;對於 HTTP 合約使用 api-design,對於濫用案例審查使用 security-review。

背景工作與佇列

簡單佇列模式

class JobQueue<T> {
  private queue: T[] = []
  private processing = false

  async add(job: T): Promise<void> {
    this.queue.push(job)

    if (!this.processing) {
      this.process()
    }
  }

  private async process(): Promise<void> {
    this.processing = true

    while (this.queue.length > 0) {
      const job = this.queue.shift()!

      try {
        await this.execute(job)
      } catch (error) {
        console.error('工作失敗:', 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: '工作已排入佇列' })
}

日誌記錄與監控

結構化日誌

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()

// 用法
export async function GET(request: Request) {
  const requestId = crypto.randomUUID()

  logger.info('正在取得市場資料', {
    requestId,
    method: 'GET',
    path: '/api/markets'
  })

  try {
    const markets = await fetchMarkets()
    return NextResponse.json({ success: true, data: markets })
  } catch (error) {
    logger.error('無法取得市場資料', error as Error, { requestId })
    return NextResponse.json({ error: '內部錯誤' }, { status: 500 })
  }
}

請記住:後端模式能夠實現可擴展、可維護的伺服器端應用程式。選擇符合你專案複雜度的模式。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

相關技能