# Backend Patterns Skill

> **API design, repository pattern, caching, and rate limiting.**

---

## API Design

### REST Principles

```
GET    /users          # List users
GET    /users/:id      # Get single user
POST   /users          # Create user
PUT    /users/:id      # Replace user
PATCH  /users/:id      # Update user fields
DELETE /users/:id      # Delete user
```

### Response Formats

```python
# Success response
{
    "data": {...},
    "meta": {
        "timestamp": "2026-01-24T12:00:00Z"
    }
}

# Error response
{
    "error": {
        "code": "VALIDATION_ERROR",
        "message": "Invalid email format",
        "details": [...]
    }
}
```

### Status Codes

| Code | Meaning | Use Case |
|------|---------|----------|
| 200 | OK | Successful GET/PUT/PATCH |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Validation error |
| 401 | Unauthorized | Missing/invalid auth |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 429 | Too Many Requests | Rate limited |
| 500 | Server Error | Unexpected error |

---

## Repository Pattern

### Interface

```python
from abc import ABC, abstractmethod

class UserRepository(ABC):
    @abstractmethod
    def get(self, user_id: str) -> User | None: ...

    @abstractmethod
    def save(self, user: User) -> User: ...

    @abstractmethod
    def delete(self, user_id: str) -> bool: ...

    @abstractmethod
    def find_by_email(self, email: str) -> User | None: ...
```

### Implementation

```python
class SqlUserRepository(UserRepository):
    def __init__(self, db: Database):
        self._db = db

    def get(self, user_id: str) -> User | None:
        row = self._db.execute(
            "SELECT * FROM users WHERE id = ?",
            (user_id,)
        ).fetchone()
        return User(**row) if row else None
```

### Benefits
- Testable (inject mock repository)
- Swappable (change database easily)
- Clean separation of concerns

---

## Caching Strategies

### Cache-Aside Pattern

```python
def get_user(user_id: str) -> User:
    # 1. Check cache
    cached = cache.get(f"user:{user_id}")
    if cached:
        return User.from_json(cached)

    # 2. Get from database
    user = repository.get(user_id)

    # 3. Populate cache
    cache.set(f"user:{user_id}", user.to_json(), ttl=3600)

    return user
```

### Cache Invalidation

```python
def update_user(user_id: str, data: UpdateData) -> User:
    user = repository.update(user_id, data)

    # Invalidate cache
    cache.delete(f"user:{user_id}")

    return user
```

### Cache Keys

```python
# Consistent key format
def cache_key(entity: str, id: str, *args) -> str:
    parts = [entity, id] + list(args)
    return ":".join(parts)

# Examples
cache_key("user", "123")              # user:123
cache_key("user", "123", "posts")     # user:123:posts
```

---

## Rate Limiting

### Token Bucket

```python
class RateLimiter:
    def __init__(self, rate: int, per_seconds: int):
        self.rate = rate
        self.per_seconds = per_seconds

    def allow(self, key: str) -> bool:
        current = cache.get(f"rate:{key}")
        if current is None:
            cache.set(f"rate:{key}", 1, ttl=self.per_seconds)
            return True

        if int(current) >= self.rate:
            return False

        cache.incr(f"rate:{key}")
        return True
```

### Usage

```python
@app.before_request
def check_rate_limit():
    limiter = RateLimiter(rate=100, per_seconds=60)
    if not limiter.allow(request.remote_addr):
        raise TooManyRequestsError()
```

---

## Database Patterns

### Transactions

```python
async def transfer_funds(from_id: str, to_id: str, amount: Decimal):
    async with db.transaction():
        from_account = await accounts.get(from_id)
        to_account = await accounts.get(to_id)

        if from_account.balance < amount:
            raise InsufficientFundsError()

        from_account.balance -= amount
        to_account.balance += amount

        await accounts.save(from_account)
        await accounts.save(to_account)
```

### Pagination

```python
@dataclass
class Page[T]:
    items: list[T]
    total: int
    page: int
    page_size: int
    has_next: bool

def paginate(
    query: Query,
    page: int = 1,
    page_size: int = 20
) -> Page:
    total = query.count()
    items = query.offset((page - 1) * page_size).limit(page_size).all()
    return Page(
        items=items,
        total=total,
        page=page,
        page_size=page_size,
        has_next=(page * page_size) < total
    )
```

---

## Related Skills

- [coding-standards](../coding-standards/) - Language best practices
- [security-review](../security-review/) - API security
