Skip to content

Fuente canónica. Cambios en el versionado de API se documentan aquí.

API Versioning System

Overview

This document describes the API versioning strategy implemented in the SaaS Courier backend.

Versioning Strategy

The API supports multiple versions coexisting simultaneously: - /api/v1/* - Version 1 (default) - /api/v2/* - Version 2 (with enhanced features)

Version Detection

Versions are detected from the URL path:

# From path (default behavior)
GET /api/v1/shipments  # Uses v1
GET /api/v2/shipments  # Uses v2

# From header (optional)
API-Version: v2

Implementation

Core Versioning Module

Located at core/versioning.py:

from core.versioning import APIVersion, resolve_version, get_api_version

# Detect version from request
@router.get("/shipments")
async def get_shipments(request: Request):
    version = resolve_version(request)
    # version is APIVersion.V1 or APIVersion.V2

Versioned DTOs

V1 and V2 DTOs are defined separately:

# V1 - modules/shipments/application/dtos.py
class ShipmentResponseDTO:
    id: str
    tenant_id: str
    tracking_id: str
    # ... basic fields

# V2 - modules/shipments/application/dtos_v2.py
class ShipmentResponseV2DTO(ShipmentResponseDTO):
    tenant_name: str = "Unknown"  # NEW: Expanded info
    version: str = "v2"           # NEW: Version marker
    available_capacity: float      # NEW: Computed field

Versioning Helper Functions

Located at modules/shipments/application/versioning.py:

from core.versioning import APIVersion
from modules.shipments.application.versioning import shipment_to_dto

@router.get("/shipments")
async def get_shipments(request: Request):
    version = resolve_version(request)
    shipment = await repo.get_by_id(id)

    return shipment_to_dto(shipment, version, tenant_name="My Tenant")

Adding New Versions

1. Create Version-Specific DTOs

Create modules/<module>/application/dtos_v{N}.py:

from pydantic import BaseModel

class MyResponseV3DTO(BaseModel):
    # New fields for v3
    new_field: str

    @classmethod
    def from_v2(cls, v2_dto: "MyResponseV2DTO") -> "MyResponseV3DTO":
        # Conversion logic
        return cls(...)

2. Register Version in Factory

Update modules/<module>/application/versioning.py:

def my_entity_to_dto(entity, version: APIVersion):
    if version == APIVersion.V3:
        return MyResponseV3DTO(...)
    elif version == APIVersion.V2:
        return MyResponseV2DTO(...)
    return MyResponseV1DTO(...)

3. Update Supported Versions

Edit core/versioning.py:

class APIVersion(Enum):
    V1 = "v1"
    V2 = "v2"
    V3 = "v3"  # Add new version

SUPPORTED_VERSIONS = [APIVersion.V1, APIVersion.V2, APIVersion.V3]

Testing

Run versioning tests:

pytest tests/core/test_versioning.py -v

Best Practices

  1. Backwards Compatibility: V1 endpoints must continue working unchanged
  2. Additive Changes: New fields in V2 should have defaults
  3. No Breaking Changes: Existing clients using V1 should not break
  4. Deprecation Notice: When deprecating versions, add X-API-Deprecated header

Migration Guide

When making breaking changes:

  1. Create new DTOs with version suffix
  2. Add new version to APIVersion enum
  3. Update resolve_version() if needed
  4. Document changes in this file
  5. Add migration tests
  6. Communicate deprecation to API consumers

Ver también: API Contract · ADR-001