Claude Agent Skill · by Affaan M

Backend Patterns

This one gives you solid patterns for building maintainable APIs and backend services. It covers the repository pattern for clean data access, service layers fo

Install
Terminal · npx
$npx skills add https://github.com/affaan-m/everything-claude-code --skill backend-patterns
Works with Paperclip

How Backend Patterns fits into a Paperclip company.

Backend Patterns drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md598 lines
Expand
---name: backend-patternsdescription: Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes.origin: ECC--- # Backend Development Patterns Backend architecture patterns and best practices for scalable server-side applications. ## When to Activate - Designing REST or GraphQL API endpoints- Implementing repository, service, or controller layers- Optimizing database queries (N+1, indexing, connection pooling)- Adding caching (Redis, in-memory, HTTP cache headers)- Setting up background jobs or async processing- Structuring error handling and validation for APIs- Building middleware (auth, logging, rate limiting) ## API Design Patterns ### RESTful API Structure ```typescript// PASS: Resource-based URLsGET    /api/markets                 # List resourcesGET    /api/markets/:id             # Get single resourcePOST   /api/markets                 # Create resourcePUT    /api/markets/:id             # Replace resourcePATCH  /api/markets/:id             # Update resourceDELETE /api/markets/:id             # Delete resource // PASS: Query parameters for filtering, sorting, paginationGET /api/markets?status=active&sort=volume&limit=20&offset=0``` ### Repository Pattern ```typescript// Abstract data access logicinterface 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  }   // Other methods...}``` ### Service Layer Pattern ```typescript// Business logic separated from data accessclass MarketService {  constructor(private marketRepo: MarketRepository) {}   async searchMarkets(query: string, limit: number = 10): Promise<Market[]> {    // 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  }}``` ### Middleware Pattern ```typescript// Request/response processing pipelineexport 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' })    }  }} // Usageexport default withAuth(async (req, res) => {  // Handler has access to req.user})``` ## Database Patterns ### Query Optimization ```typescript// PASS: GOOD: Select only needed columnsconst { data } = await supabase  .from('markets')  .select('id, name, status, volume')  .eq('status', 'active')  .order('volume', { ascending: false })  .limit(10) // FAIL: BAD: Select everythingconst { data } = await supabase  .from('markets')  .select('*')``` ### N+1 Query Prevention ```typescript// FAIL: BAD: N+1 query problemconst markets = await getMarkets()for (const market of markets) {  market.creator = await getUser(market.creator_id)  // N queries} // PASS: GOOD: Batch fetchconst markets = await getMarkets()const creatorIds = markets.map(m => m.creator_id)const creators = await getUsers(creatorIds)  // 1 queryconst creatorMap = new Map(creators.map(c => [c.id, c])) markets.forEach(market => {  market.creator = creatorMap.get(market.creator_id)})``` ### Transaction Pattern ```typescriptasync 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 SupabaseCREATE OR REPLACE FUNCTION create_market_with_position(  market_data jsonb,  position_data jsonb)RETURNS jsonbLANGUAGE plpgsqlAS $$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;$$;``` ## Caching Strategies ### Redis Caching Layer ```typescriptclass CachedMarketRepository implements MarketRepository {  constructor(    private baseRepo: MarketRepository,    private redis: RedisClient  ) {}   async findById(id: string): Promise<Market | 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): Promise<void> {    await this.redis.del(`market:${id}`)  }}``` ### Cache-Aside Pattern ```typescriptasync function getMarketWithCache(id: string): Promise<Market> {  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}``` ## Error Handling Patterns ### Centralized Error Handler ```typescriptclass 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 })} // Usageexport 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 ```typescriptasync 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) {        // Exponential backoff: 1s, 2s, 4s        const delay = Math.pow(2, i) * 1000        await new Promise(resolve => setTimeout(resolve, delay))      }    }  }   throw lastError!} // Usageconst data = await fetchWithRetry(() => fetchFromAPI())``` ## Authentication & Authorization ### JWT Token Validation ```typescriptimport 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 routeexport async function GET(request: Request) {  const user = await requireAuth(request)   const data = await getDataForUser(user.userId)   return NextResponse.json({ success: true, data })}``` ### Role-Based Access Control ```typescripttype 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, 'Insufficient permissions')      }       return handler(request, user)    }  }} // Usage - HOF wraps the handlerexport const DELETE = requirePermission('delete')(  async (request: Request, user: User) => {    // Handler receives authenticated user with verified permission    return new Response('Deleted', { status: 200 })  })``` ## Rate Limiting ### Simple In-Memory Rate Limiter ```typescriptclass 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) || []     // 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}``` ## Background Jobs & Queues ### Simple Queue Pattern ```typescriptclass 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('Job failed:', error)      }    }     this.processing = false  }   private async execute(job: T): Promise<void> {    // Job execution logic  }} // Usage for indexing marketsinterface IndexJob {  marketId: string} const indexQueue = new JobQueue<IndexJob>() 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' })}``` ## Logging & Monitoring ### Structured Logging ```typescriptinterface 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() // Usageexport 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 })  }}``` **Remember**: Backend patterns enable scalable, maintainable server-side applications. Choose patterns that fit your complexity level.