<< All versions
Skill v1.0.1
currentAutomated scan100/100davila7/claude-code-templates/cc-skill-backend-patterns
4 files
──Details
PublishedOctober 30, 2026 at 10:07 AM
Content Hashsha256:fcca3480a6477ac0...
Git SHA807d769ee007
Bump Typepatch
──Files
Files (1 file, 12.9 KB)
SKILL.md12.9 KBactive
SKILL.md · 585 lines · 12.9 KB
name: cc-skill-backend-patterns description: Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes. metadata: author: affaan-m version: "1.0"
Backend Development Patterns
Backend architecture patterns and best practices for scalable server-side applications.
API Design Patterns
RESTful API Structure
typescript
// ✅ 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// ✅ 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 queryif (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 logicconst embedding = await generateEmbedding(query)const results = await this.vectorSearch(embedding, limit)// Fetch full dataconst markets = await this.marketRepo.findByIds(results.map(r => r.id))// Sort by similarityreturn 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) {// 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 = userreturn 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
// ✅ GOOD: Select only needed columnsconst { data } = await supabase.from('markets').select('id, name, status, volume').eq('status', 'active').order('volume', { ascending: false }).limit(10)// ❌ BAD: Select everythingconst { data } = await supabase.from('markets').select('*')
N+1 Query Prevention
typescript
// ❌ BAD: N+1 query problemconst markets = await getMarkets()for (const market of markets) {market.creator = await getUser(market.creator_id) // N queries}// ✅ 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
typescript
async function createMarketWithPosition(marketData: CreateMarketDto,positionData: CreatePositionDto) {// Use Supabase transactionconst { 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 automaticallyINSERT INTO markets VALUES (market_data);INSERT INTO positions VALUES (position_data);RETURN jsonb_build_object('success', true);EXCEPTIONWHEN OTHERS THEN-- Rollback happens automaticallyRETURN jsonb_build_object('success', false, 'error', SQLERRM);END;$$;
Caching Strategies
Redis Caching Layer
typescript
class CachedMarketRepository implements MarketRepository {constructor(private baseRepo: MarketRepository,private redis: RedisClient) {}async findById(id: string): Promise<Market | null> {// Check cache firstconst cached = await this.redis.get(`market:${id}`)if (cached) {return JSON.parse(cached)}// Cache miss - fetch from databaseconst market = await this.baseRepo.findById(id)if (market) {// Cache for 5 minutesawait 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}`// Try cacheconst cached = await redis.get(cacheKey)if (cached) return JSON.parse(cached)// Cache miss - fetch from DBconst market = await db.markets.findUnique({ where: { id } })if (!market) throw new Error('Market not found')// Update cacheawait redis.setex(cacheKey, 300, JSON.stringify(market))return market}
Error Handling Patterns
Centralized Error Handler
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 })}// Log unexpected errorsconsole.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
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) {// Exponential backoff: 1s, 2s, 4sconst delay = Math.pow(2, i) * 1000await new Promise(resolve => setTimeout(resolve, delay))}}}throw lastError!}// Usageconst data = await fetchWithRetry(() => fetchFromAPI())
Authentication & Authorization
JWT Token Validation
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)}// 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
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 async (request: Request) => {const user = await requireAuth(request)if (!hasPermission(user, permission)) {throw new ApiError(403, 'Insufficient permissions')}return user}}// Usageexport const DELETE = requirePermission('delete')(async (request: Request) => {// Handler with permission check})
Rate Limiting
Simple In-Memory Rate Limiter
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) || []// Remove old requests outside windowconst recentRequests = requests.filter(time => now - time < windowMs)if (recentRequests.length >= maxRequests) {return false // Rate limit exceeded}// Add current requestrecentRequests.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/minif (!allowed) {return NextResponse.json({error: 'Rate limit exceeded'}, { status: 429 })}// Continue with request}
Background Jobs & Queues
Simple Queue Pattern
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> {// 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 blockingawait indexQueue.add({ marketId })return NextResponse.json({ success: true, message: 'Job queued' })}
Logging & Monitoring
Structured Logging
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()// 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.