← Back to list

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