DEV Community

The AI producer
The AI producer

Posted on

7 API Design Patterns Every Developer Should Know (With Real Code Examples)

7 API Design Patterns Every Developer Should Know (With Real Code Examples)

I've been building and consuming APIs for years, and I keep seeing the same mistakes repeated. Meanwhile, the developers who ship fast all use a handful of patterns that make their lives 10x easier.

Here are the 7 patterns I use on every project, with copy-paste code examples.


1. Paginated Collections (Cursor-Based)

Forget offset pagination for anything real. Cursor-based pagination is the standard for modern APIs.

@app.get("/api/items")
async def list_items(cursor: str = None, limit: int = 50):
    query = db.query(Item).order_by(Item.created_at.desc())
    if cursor:
        cursor_time = base64.urlsafe_decode(cursor)
        query = query.filter(Item.created_at < cursor_time)
    items = query.limit(limit + 1).all()

    next_cursor = None
    if len(items) > limit:
        next_cursor = base64.urlsafe_encode(str(items[-1].created_at))
        items = items[:limit]

    return {"items": items, "next_cursor": next_cursor}
Enter fullscreen mode Exit fullscreen mode

Why: Offset pagination breaks when items are inserted/deleted between requests. Cursor-based is stable and performant.


2. Rate Limiting with Sliding Window

Fixed-window rate limits create a thundering herd at window boundaries. Sliding window is smoother.

import time
from collections import defaultdict

rate_limits = defaultdict(list)

def check_rate_limit(user_id: str, max_requests: int = 100, window: int = 3600):
    now = time.time()
    # Clean old entries
    rate_limits[user_id] = [t for t in rate_limits[user_id] if now - t < window]

    if len(rate_limits[user_id]) >= max_requests:
        return False
    rate_limits[user_id].append(now)
    return True
Enter fullscreen mode Exit fullscreen mode

Production tip: Use Redis sorted sets for this instead of in-memory storage.


3. Optimistic Concurrency with ETags

Prevent lost updates without locking.

@app.put("/api/items/{item_id}")
async def update_item(item_id: int, data: ItemUpdate, if_match: str):
    item = db.get(Item, item_id)
    current_etag = f'"{hash(item.to_dict())}"'

    if if_match != current_etag:
        raise HTTPException(409, "Item was modified by another request")

    # Apply update
    for key, value in data.dict(exclude_unset=True).items():
        setattr(item, key, value)
    db.commit()

    return {"etag": current_etag, "item": item}
Enter fullscreen mode Exit fullscreen mode

4. Bulk Operations with Batching

Individual API calls for bulk work are painfully slow.

@app.post("/api/items/bulk")
async def bulk_create(items: list[ItemCreate], batch_size: int = 100):
    results = []
    errors = []

    for i in range(0, len(items), batch_size):
        batch = items[i:i + batch_size]
        try:
            created = db.bulk_insert(Item, [item.dict() for item in batch])
            results.extend(created)
        except Exception as e:
            errors.append({"batch": i // batch_size, "error": str(e)})

    return {"created": len(results), "errors": errors}
Enter fullscreen mode Exit fullscreen mode

5. Webhook Delivery with Retry

If you're sending webhooks, you need retries with exponential backoff.

import httpx
import asyncio

async def deliver_webhook(url: str, payload: dict, max_retries: int = 3):
    for attempt in range(max_retries):
        try:
            response = httpx.post(url, json=payload, timeout=10)
            if response.status_code < 500:
                return {"status": "delivered", "code": response.status_code}
        except httpx.TimeoutException:
            pass

        if attempt < max_retries - 1:
            await asyncio.sleep(2 ** attempt)  # 1s, 2s, 4s

    return {"status": "failed", "attempts": max_retries}
Enter fullscreen mode Exit fullscreen mode

6. Field Selection (Sparse Fieldsets)

Let clients request only the fields they need.

@app.get("/api/users/{user_id}")
async def get_user(user_id: int, fields: str = None):
    user = db.get(User, user_id)
    all_fields = user.to_dict()

    if fields:
        requested = set(fields.split(","))
        return {k: v for k, v in all_fields.items() if k in requested}

    return all_fields
Enter fullscreen mode Exit fullscreen mode

Call: GET /api/users/42?fields=id,name,email → only returns those 3 fields.


7. Health Check with Dependency Status

A real health check tests actual dependencies, not just "I'm alive."

@app.get("/health")
async def health_check():
    checks = {}

    # Check database
    try:
        db.execute("SELECT 1")
        checks["database"] = "ok"
    except Exception as e:
        checks["database"] = f"error: {str(e)[:50]}"

    # Check Redis
    try:
        redis.ping()
        checks["cache"] = "ok"
    except Exception:
        checks["cache"] = "unavailable"

    status = 200 if all(v == "ok" for v in checks.values()) else 503
    return JSONResponse(status_code=status, content={
        "status": "healthy" if status == 200 else "degraded",
        "checks": checks,
        "version": "1.2.0"
    })
Enter fullscreen mode Exit fullscreen mode

The Full Toolkit

These patterns are part of a larger collection I put together. If you work with APIs daily, here's what I've built:

📚 The Developer's API Cheat Sheet Collection — Volume 2 ($9.99) — 200+ API patterns, auth flows, error handling, and code snippets for REST + GraphQL

📋 Developer API Cheat Sheet Collection — 50+ APIs with Code Examples ($4.99) — Quick reference for 50+ popular APIs (Stripe, Twilio, SendGrid, etc.)

🔧 The Complete Git & GitHub Power User Toolkit ($9.99) — 200+ Git commands, hooks, CI/CD templates for API project workflows

🐳 50 Docker Commands Every Developer Should Know ($4.99) — Containerize your APIs with these 50 production-ready Docker commands

🎁 10 AI Prompts That Save Me 5 Hours/Week (FREE) — AI prompts for code review, debugging, and API documentation


One More Thing

If you're building side projects and want to monetize them, I put together a guide on automating income streams:

💡 Complete Guide to Building Passive Income with AI Automation ($6.99)


What patterns do you use?

Did I miss any API patterns you can't live without? Drop them in the comments — I might add them to Volume 3 of the cheat sheet collection.


Originally published as part of the Developer Productivity series.

Top comments (0)