Mapping Data Models
Translate cleanly between an external API's shapes and your own internal model.
What you'll learn
- Explain why a translation layer is worth the effort
- Map fields, types and naming between models
- Handle missing, extra and renamed fields
- Isolate external shapes from your domain
- Keep mappings testable and maintainable
6 min
Two models, one boundary
An external API has its own data shapes — its field names, types, structures and conventions — and your application has its own internal model. These rarely match exactly, and they should not have to. A mapping layer translates between them at the boundary.
The temptation is to skip this and let the API's shapes flow straight into your code. It is quicker at first and quietly corrosive: every part of your app becomes coupled to an external format you do not control. When the API changes, the damage spreads everywhere. A deliberate translation layer keeps the two worlds separate so each can evolve on its own terms.
What mapping involves
Translation is more than copying fields across. Common transformations include:
- Renaming — the API's naming convention may differ from yours.
- Type conversion — strings to dates, numbers to enums, units normalised.
- Restructuring — flattening nested shapes or composing flat ones.
- Defaulting — supplying sensible values where the API omits a field.
The mapping is where you impose your own consistency on someone else's choices. Done well, the rest of your code sees clean, predictable objects in your own vocabulary, with the API's quirks absorbed at the edge rather than leaking inward. Keep the rules in one place so they are easy to follow.
Handling mismatches
Models drift, so mapping must cope with fields that are missing, unexpected, or renamed without breaking. Be deliberate at the boundary.
def to_account(payload):
return Account(
id=payload['id'],
name=payload.get('name', ''),
# unknown fields are simply ignored
)Tolerate unknown fields rather than failing on them, so the API adding something new does not break you. Decide explicitly how to treat a missing field — a sensible default, or a clear error if it is truly required. Validate that what you received is usable before you act on it. This defensiveness is what lets your integration survive the API evolving, which it inevitably will. See error handling for malformed responses.
Keep it isolated and tested
Concentrate all mapping in one place — a dedicated module or function per resource — rather than scattering field access throughout the app. This single seam is where external and internal meet, and keeping it small makes API changes a localised edit instead of a sprawling one.
Mapping is also highly testable, so test it thoroughly. Feed in representative payloads — including ones with missing or unexpected fields — and assert you get the right internal objects out. Because mapping is pure translation with no side effects, these tests are fast and reliable, and they catch a whole class of subtle bugs. A well-tested mapping layer is one of the cheapest forms of insurance against an evolving API. It pairs naturally with a client wrapper.
Key takeaways
- A mapping layer translates between external shapes and your internal model
- Mapping renames, converts types, restructures and supplies defaults
- Tolerate unknown fields and decide deliberately how to handle missing ones
- Concentrate mapping in one seam so API changes stay localised
- Mapping is pure and highly testable — cover it thoroughly
FAQ
Why not use the API's data shapes directly?
Because it couples your whole application to a format you do not control. When the API changes, the impact spreads everywhere. A mapping layer keeps external and internal models separate so each can evolve independently.
How should I handle a field the API stopped sending?
Decide deliberately at the mapping boundary: supply a sensible default if the field is optional, or raise a clear error if it is genuinely required. Tolerate unknown fields too, so new additions do not break you.
How do I test a mapping layer?
Feed representative payloads — including ones with missing or unexpected fields — into the mapping and assert the internal objects produced. Because mapping is pure translation with no side effects, these tests are fast and reliable.
Ready to build?
Read the API reference, grab the OpenAPI spec, and ship a resilient integration.