Back to list
yairbederman

backend-api-patterns

by yairbederman

0🍴 0📅 Jan 19, 2026

SKILL.md


name: backend-api-patterns description: Backend API implementation patterns for scalability, security, and maintainability. Use when building APIs, services, and backend systems.

Backend API Patterns

Implementation patterns for scalable, secure, and maintainable backend systems.

When to Use

  • Building new API endpoints
  • Implementing service layer logic
  • Database operations and queries
  • Authentication and authorization

Service Layer Patterns

Repository Pattern

// Separate data access from business logic
interface UserRepository {
  findById(id: string): Promise<User | null>
  findByEmail(email: string): Promise<User | null>
  create(data: CreateUserDto): Promise<User>
  update(id: string, data: UpdateUserDto): Promise<User>
  delete(id: string): Promise<void>
}

class UserService {
  constructor(private readonly userRepo: UserRepository) {}
  
  async createUser(dto: CreateUserDto): Promise<User> {
    // Business logic here
    const existing = await this.userRepo.findByEmail(dto.email)
    if (existing) throw new ConflictError('Email already exists')
    return this.userRepo.create(dto)
  }
}

Unit of Work Pattern

// Manage transactions across repositories
interface UnitOfWork {
  users: UserRepository
  orders: OrderRepository
  commit(): Promise<void>
  rollback(): Promise<void>
}

async function createOrderWithUser(uow: UnitOfWork, data: OrderData) {
  try {
    const user = await uow.users.create(data.user)
    const order = await uow.orders.create({ ...data.order, userId: user.id })
    await uow.commit()
    return { user, order }
  } catch (error) {
    await uow.rollback()
    throw error
  }
}

Error Handling

Custom Error Classes

abstract class AppError extends Error {
  abstract statusCode: number
  abstract code: string
}

class NotFoundError extends AppError {
  statusCode = 404
  code = 'NOT_FOUND'
  constructor(resource: string, id: string) {
    super(`${resource} with id ${id} not found`)
  }
}

class ValidationError extends AppError {
  statusCode = 422
  code = 'VALIDATION_ERROR'
  constructor(public errors: FieldError[]) {
    super('Validation failed')
  }
}

Global Error Handler

function errorHandler(error: Error, req: Request, res: Response, next: Next) {
  if (error instanceof AppError) {
    return res.status(error.statusCode).json({
      error: {
        code: error.code,
        message: error.message,
        details: error instanceof ValidationError ? error.errors : undefined
      }
    })
  }
  
  // Log unexpected errors
  logger.error('Unexpected error', { error, requestId: req.id })
  
  return res.status(500).json({
    error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' }
  })
}

Database Patterns

Query Optimization

// ❌ N+1 Query Problem
const users = await db.users.findMany()
for (const user of users) {
  user.orders = await db.orders.findMany({ where: { userId: user.id } })
}

// ✅ Eager Loading
const users = await db.users.findMany({
  include: { orders: true }
})

// ✅ Or batch loading
const users = await db.users.findMany()
const orders = await db.orders.findMany({
  where: { userId: { in: users.map(u => u.id) } }
})

Connection Pooling

const pool = {
  min: 2,
  max: 10,
  idleTimeoutMs: 30000,
  acquireTimeoutMs: 30000
}

Security Patterns

Input Validation

// Always validate at the boundary
const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2).max(100),
  password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/)
})

async function createUser(req: Request) {
  const validated = CreateUserSchema.parse(req.body)
  // Safe to use validated data
}

Authorization

// Resource-level authorization
async function getOrder(userId: string, orderId: string) {
  const order = await orderRepo.findById(orderId)
  if (!order) throw new NotFoundError('Order', orderId)
  if (order.userId !== userId) throw new ForbiddenError()
  return order
}

Performance Patterns

Caching Strategy

async function getUserById(id: string): Promise<User> {
  // Check cache first
  const cached = await cache.get(`user:${id}`)
  if (cached) return JSON.parse(cached)
  
  // Fetch from database
  const user = await userRepo.findById(id)
  if (!user) throw new NotFoundError('User', id)
  
  // Store in cache with TTL
  await cache.set(`user:${id}`, JSON.stringify(user), 'EX', 3600)
  return user
}

Rate Limiting

const rateLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per window
  message: { error: { code: 'RATE_LIMITED', message: 'Too many requests' } }
})

Logging Standards

// Structured logging
logger.info('User created', {
  userId: user.id,
  email: user.email,
  requestId: req.id,
  duration: Date.now() - startTime
})

// Log levels
logger.debug() // Development details
logger.info()  // Business events
logger.warn()  // Potential issues
logger.error() // Errors needing attention

Checklist

Before completing backend implementation:

  • Input validated with schema
  • Errors properly categorized
  • Database queries optimized (no N+1)
  • Authorization checks in place
  • Sensitive data not logged
  • Transaction boundaries defined
  • Caching strategy considered
  • Rate limiting configured

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon