PYTHON

Transforming External API Responses to Internal Data Models

Learn to map and transform complex external API data structures into simpler, normalized internal data models for better application logic and consistency using Python.

import requests
import json

# --- Simulated External API Response ---
# In a real scenario, this would come from requests.get('...')
raw_api_data = {
    "id": "USR_12345",
    "personal_info": {
        "first_name": "Alice",
        "last_name": "Smith",
        "email_address": "[email protected]",
        "contact_phone": "+1-555-123-4567"
    },
    "account_status": {
        "is_active": True,
        "joined_date": "2023-01-15T10:00:00Z"
    },
    "address_details": {
        "street": "123 Main St",
        "city": "Anytown",
        "zip_code": "90210",
        "country": "USA"
    },
    "preferences": [
        {"type": "newsletter", "value": True},
        {"type": "notifications", "value": "email"},
    ]
}

# --- Internal Data Model Definition (Simplified) ---
class UserProfile:
    def __init__(self, user_id, first_name, last_name, email, phone, is_active, joined_at, address):
        self.user_id = user_id
        self.first_name = first_name
        self.last_name = last_name
        self.email = email
        self.phone = phone
        self.is_active = is_active
        self.joined_at = joined_at
        self.address = address # Can be another nested object/dict

    def to_dict(self):
        return {
            "user_id": self.user_id,
            "first_name": self.first_name,
            "last_name": self.last_name,
            "email": self.email,
            "phone": self.phone,
            "is_active": self.is_active,
            "joined_at": self.joined_at,
            "address": self.address
        }

    def __repr__(self):
        return f"<UserProfile: {self.first_name} {self.last_name} ({self.user_id})>"

# --- Transformation Function ---
def transform_api_user_data(api_data: dict) -> UserProfile:
    """
    Transforms raw API user data into a standardized UserProfile object.
    """
    try:
        user_id = api_data["id"]
        first_name = api_data["personal_info"]["first_name"]
        last_name = api_data["personal_info"]["last_name"]
        email = api_data["personal_info"]["email_address"]
        phone = api_data["personal_info"].get("contact_phone", "N/A") # Use .get for optional fields

        is_active = api_data["account_status"]["is_active"]
        joined_at = api_data["account_status"]["joined_date"]

        address = {
            "street": api_data["address_details"]["street"],
            "city": api_data["address_details"]["city"],
            "zip": api_data["address_details"]["zip_code"],
            "country": api_data["address_details"]["country"]
        }

        # If you needed to process preferences, you'd do it here
        # newsletter_pref = next((p['value'] for p in api_data['preferences'] if p['type'] == 'newsletter'), False)

        return UserProfile(
            user_id=user_id,
            first_name=first_name,
            last_name=last_name,
            email=email,
            phone=phone,
            is_active=is_active,
            joined_at=joined_at,
            address=address
        )
    except KeyError as e:
        raise ValueError(f"Missing expected field in API data: {e}")
    except Exception as e:
        raise RuntimeError(f"Error transforming API data: {e}")

# --- Usage Example ---
def get_and_transform_user(user_id: str):
    # In a real app:
    # response = requests.get(f"https://api.external.com/users/{user_id}")
    # raw_data = response.json()
    raw_data = raw_api_data # Using our simulated data for demonstration

    user_profile = transform_api_user_data(raw_data)
    print(f"Transformed User: {user_profile.to_dict()}")
    return user_profile

if __name__ == "__main__":
    transformed_user = get_and_transform_user("USR_12345")
    # Access attributes
    print(f"User ID: {transformed_user.user_id}")
    print(f"Email: {transformed_user.email}")
    print(f"Address City: {transformed_user.address['city']}")

    # Example of handling a missing field gracefully
    simulated_data_missing_phone = {**raw_api_data, "personal_info": {**raw_api_data["personal_info"], "contact_phone": None}}
    simulated_data_missing_phone["personal_info"].pop("contact_phone") # Remove it entirely
    try:
        user_with_missing_phone = transform_api_user_data(simulated_data_missing_phone)
        print(f"
User with missing phone (handled): {user_with_missing_phone.phone}")
    except Exception as e:
        print(f"
Error transforming data with missing phone: {e}")

    # Example of handling a critical missing field
    simulated_data_missing_email = {**raw_api_data, "personal_info": {**raw_api_data["personal_info"], "email_address": None}}
    simulated_data_missing_email["personal_info"].pop("email_address")
    try:
        transform_api_user_data(simulated_data_missing_email)
    except Exception as e:
        print(f"
Error transforming data with critically missing email: {e}")
How it works: This Python snippet demonstrates a robust method for transforming data received from an external API into a standardized internal data model (`UserProfile` class). External APIs often return data with inconsistent naming conventions, nested structures, or irrelevant fields. By implementing a dedicated transformation function, developers can map, rename, and normalize API response fields, ensuring that the application works with a consistent and predictable data structure. This improves code readability, maintainability, and makes the application more resilient to changes in the external API's response format.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs