API Design & Contracts

When Clients Started Breaking

Three months into production, I needed to add a field to the order response. Simple change, right?

# Before
class OrderResponse(BaseModel):
    id: int
    total: float
    status: str

# After - Added items field
class OrderResponse(BaseModel):
    id: int
    total: float
    status: str
    items: List[OrderItem]  # New field

I deployed. Within minutes, clients started failing. Why? Some clients were using strict schema validation and rejected responses with unexpected fields. Others expected items to always be present and crashed when calling old endpoints.

That day I learned: APIs are contracts. Breaking them costs money, trust, and sleep.

REST Principles with FastAPI

Here's how I design APIs for the Restaurant Service:

Resource-Oriented URLs

HTTP Status Codes (Use Them Correctly!)

Request/Response Models with Pydantic

Pydantic models are your contract. Make them explicit and strict:

OpenAPI Automatic Generation

FastAPI generates OpenAPI docs automatically. Make them useful:

API Versioning Strategies

I learned about versioning the hard way. Here are three approaches:

Strategy 1: URL Versioning (What I Use)

Strategy 2: Header Versioning

Strategy 3: Content Negotiation (Media Type)

Breaking vs Non-Breaking Changes

Managing API Evolution

Here's how I manage multiple versions in production:

Backward Compatibility Techniques

Key Learnings

  1. APIs are contracts—treat breaking changes seriously

    • Version your API from day one

    • Never break existing clients

  2. Make invalid states unrepresentable

    • Use Pydantic validation

    • Fail fast with clear errors

  3. Documentation is part of the contract

    • OpenAPI docs should be accurate

    • Include examples for every endpoint

  4. Prefer additive changes

    • Add new fields as optional

    • Deprecate before removing

  5. Version explicitly in URL

    • Clear, visible, easy to test

    • Clients know exactly what they're using

Common Mistakes

  1. No versioning from the start

    • Adding versioning later is painful

    • Start with /api/v1 on day one

  2. Changing response shapes without versioning

    • Breaks clients silently

    • Always introduce as new version

  3. Weak request validation

    • Accepting invalid data causes bugs downstream

    • Validate strictly at API boundary

  4. Inconsistent error responses

    • Different endpoints return different error formats

    • Standardize error structure

When to Use These Patterns

URL Versioning:

  • Most REST APIs

  • Clear deprecation path needed

  • Multiple versions in production

Header Versioning:

  • Internal APIs

  • Need stable URLs

  • Complex routing logic

Media Type Versioning:

  • Following strict REST

  • Content negotiation important

  • Rare in practice

Next Steps

Now that you understand API design, the next article explores authentication and authorization architecture—how to secure these APIs and manage user permissions.

Next Article: 06-authentication-authorization-architecture.md


Remember: Your API is a product. Breaking it breaks your users' code. Version early, deprecate gracefully, and never surprise your clients.

Last updated