API Design · REST · Data Modeling · API Architecture · Small Teams
API Data Modeling for Small Teams: Flat vs. Nested Structures and When to Stop Over-Normalizing
A practical guide for indie developers and small software teams on modeling API resources—choosing between flat and nested responses, using hypermedia links wisely, handling polymorphic types, and avoiding the trap of over-normalization.
Published:
The Short Answer
For most small API teams, start with flat, denormalized responses for simple CRUD operations and listing endpoints. Reserve nested structures for cases where the hierarchy is intrinsic to the domain and the frontend genuinely benefits from grouped data. Use hypermedia links (like links or _embedded) when relationships matter more than duplication. Avoid over-normalizing your database schema into your API surface—your API is not your database.
This isn’t a matter of personal preference alone; it’s about matching the response shape to the consumer’s actual access patterns. The best API data modeling decisions come from observing how your endpoints will be queried, not from theoretical purity.
Why Resource Modeling Matters More for Small Teams
When you’re a team of two or three developers shipping an API that other people depend on, every modeling decision carries outsized weight. Unlike large organizations with dedicated API product teams, small teams don’t have the luxury of iterating on a poorly designed data model through multiple major versions. A bad resource model early on becomes a debt that compounds with every new consumer.
The API-first principle, famously mandated at Amazon around 2002, requires that all teams expose their data and functionality through service interfaces. But the mandate says nothing about how you structure the data inside those interfaces. That’s where small teams often stumble—confusing database normalization with API design.
As the Swagger documentation on REST best practices notes, resources are fundamental to REST. A resource is an object important enough to be referenced in itself, with data, relationships, and methods that operate against it. A group of resources is a collection. The way you expose these through URLs and response bodies is where the real design work happens.
Flat Responses: When Simplicity Wins
A flat (denormalized) response structure places all relevant fields at the top level of the JSON object. Consider a customer endpoint:
{
"id": 5,
"first_name": "Cole",
"last_name": "Palmer",
"email": "cole@gmail.com",
"phone": "880147258369",
"business_name": "Palmer Leather & Shoes",
"website": "palmer-leather.com"
}
This approach has clear advantages for small teams:
- Shallow access paths. Consumers read
customer.emailinstead of navigatingcustomer.contact.email. For listing endpoints and data tables, this reduces client-side processing. - Easier pagination and filtering. When every field you might filter on sits at the top level, you can build query parameters without worrying about nested object traversal.
- Predictable serialization. There’s no risk of a consumer accidentally receiving a deeply nested object when they expected a flat record.
The LinkedIn analysis of flat versus nested structures notes that flat responses are “ideal for simple CRUD endpoints and data tables” and that many backend developers personally prefer this approach for “most internal and straightforward APIs.”
Use flat responses when:
- You’re building internal APIs or straightforward CRUD operations
- The same resource appears in multiple contexts with slightly different shapes
- Your consumers primarily need to list, filter, and sort data
- Bandwidth and parsing speed matter more than structural elegance
Nested Responses: When Hierarchy Adds Value
A nested (hierarchical) response structure groups related fields into logical objects:
{
"id": 5,
"name": {
"first_name": "Cole",
"last_name": "Palmer"
},
"contact": {
"email": "cole@gmail.com",
"phone": "880147258369"
},
"business_details": {
"name": "Palmer Leather & Shoes",
"website": "palmer-leather.com"
}
}
Nested structures serve a purpose. The Moesif guide on REST nested resources explains that nested URLs like /posts/:postId/comments/:commentId convey a hierarchical relationship that flat URLs like /comments/:commentId cannot. This matters for readability and debugging—when an API consumer sees a nested path, they immediately understand ownership and context.
The Stack Exchange discussion on flat versus nested JSON for hierarchical data highlights that nested structures are “already in a usable format” and “save some bandwidth” even after gzip compression. For domain-rich applications where the hierarchy reflects real-world relationships, nesting reduces the cognitive load on consumers who already think in terms of objects and components.
Use nested responses when:
- Your domain model is inherently hierarchical (organizations with departments, folders with files)
- Grouping improves semantic clarity and the frontend renders the data in corresponding components
- You’re building a public API where the structure communicates domain boundaries
- The same nested object appears consistently across multiple endpoints
The Nested Resource URL Debate
One of the most common points of confusion for small teams is whether to use nested URLs. The Stack Overflow discussion on REST nested resources illustrates this perfectly. Consider a hierarchy where companies own departments and departments own employees:
/companies/{companyId}/departments/{departmentId}/employees/{empId}
The problem arises when you need to list all employees across all companies. A purely nested design forces you into awkward workarounds. The Moesif article points out that nested resources can create an “appearance of hierarchical relationship” even when the underlying data model is many-to-many. GitHub’s API, for example, exposes both /users/:userName/repos and /repos/:repoName/users to represent a many-to-many relationship from both directions.
Here’s the practical guidance for small teams:
-
Give each resource a canonical path. An employee should be addressable at
/employees/{id}regardless of which company they belong to. Nested URLs are useful for scoping queries but shouldn’t be the only way to access a resource. -
Use nested URLs for context, not ownership.
/companies/{id}/departmentsis useful when you’re listing departments within a specific company. But/departments/{id}should also work when you need a department in isolation. -
Avoid deep nesting beyond two levels. As the Stack Overflow thread notes, paths like
/companies/{companyId}/departments/{departmentId}/employees/{empId}become unwieldy quickly. If you find yourself nesting three or four levels deep, you’re likely modeling the wrong abstraction.
Hypermedia Links: The Third Option
Neither flat nor nested is always the answer. Sometimes the best approach is to keep resources flat at the top level and use hypermedia links to express relationships. This is the approach recommended by the HAL (Hypertext Application Language) specification and used by APIs like GitHub’s.
{
"id": 5,
"name": "Cole Palmer",
"email": "cole@gmail.com",
"links": {
"company": "/companies/42",
"department": "/departments/7"
}
}
This approach gives you the simplicity of flat responses with the flexibility of explicit relationships. Consumers can follow links when they need related data, but they aren’t forced to parse nested objects they don’t need.
The key insight from the Medium article on database normalization versus denormalization is that your API should not mirror your database schema. A normalized database with foreign keys is excellent for data integrity. But an API that exposes those same foreign keys as nested lookups forces every consumer to make additional requests or parse unnecessary structure.
Hypermedia links let you serve denormalized data at the API surface while preserving the normalized structure in your database. This is the sweet spot for small teams: your database stays clean, and your API stays simple.
Polymorphic Types: Don’t Hide Them Behind Nesting
Small teams often encounter polymorphic relationships—where a single field can reference different resource types. A common example is a notification system where a notification can reference either an order or a message.
The temptation is to nest polymorphic types inside a single object:
{
"id": 101,
"type": "order",
"related": {
"order_id": 42,
"total": 150.00,
"status": "shipped"
}
}
This is a modeling trap. The nested related object changes shape depending on the type field, which makes client-side parsing fragile and documentation painful. Instead, use a consistent flat structure with a type discriminator:
{
"id": 101,
"type": "order",
"related_id": 42,
"related_type": "order"
}
Or better yet, use separate endpoints and let the consumer request the resource they need:
GET /notifications/101
GET /orders/42
The Swagger best practices guide emphasizes that URLs should be “neat, elegant, and simple so that developers using your product can easily use them.” Polymorphic nesting violates that principle by making the response shape unpredictable.
The Over-Normalization Trap
The most common mistake small API teams make is over-normalizing their response structures. This usually stems from one of two habits:
Habit 1: Mirroring the database. If your database has a users table, a profiles table, and a settings table, you might be tempted to expose them as three nested objects. But your API consumers don’t care about your table structure. They care about what data they need for a given operation. The Medium article on normalization versus denormalization makes clear that denormalization at the API layer is often the right choice—even if it means duplicating a few fields.
Habit 2: Premature abstraction. Small teams sometimes design APIs for a future that may never arrive. They create deeply nested structures “just in case” the frontend needs to group data differently later. This adds complexity for zero immediate benefit. The Swagger code-first versus design-first article notes that a code-first approach can suit “rapid prototyping, small teams, or projects with highly iterative development” precisely because it avoids over-engineering. Start simple. Add nesting only when a consumer pattern demands it.
A practical test for over-normalization: if your API response requires the consumer to make three or more follow-up requests to assemble a single view, you’ve over-normalized. Flatten the response or add a dedicated endpoint that returns the assembled view.
Concrete Steps for Your Next API Design
-
Map your consumer access patterns first. Before writing a single endpoint, list the top ten queries your API will receive. If eight of them are “give me a list of X with fields A, B, and C,” start with flat responses.
-
Start flat, nest only when justified. Design your first version with flat responses. Introduce nesting only when you have a concrete consumer request that flat structures can’t serve efficiently.
-
Use hypermedia for relationships, not nesting for everything. If resource A references resource B, add a link. Don’t embed B inside A unless the embedding is genuinely useful to every consumer.
-
Give resources canonical paths. Every resource should be addressable at its own top-level URL, regardless of how it’s scoped in nested queries.
-
Document the shape, not the schema. Your OpenAPI spec should describe what consumers get, not what your database contains. The Swagger design-first approach—creating a detailed API definition before writing code—helps keep this distinction clear.
-
Test with real consumers early. The federated API management article from Kong emphasizes that API platforms should “raise engineering standards and build higher-quality APIs.” The best way to validate your modeling decisions is to have actual consumers use the API before you commit to a structure.
FAQ
Q: Should I use nested URLs if my database has foreign keys? A: No. Foreign keys are a database concern. Use nested URLs for scoping and readability, but always provide a canonical top-level path for each resource. The Moesif article warns that nested URLs can create a misleading “appearance of hierarchical relationship” when the underlying data model is more complex.
Q: How do I handle pagination with nested resources?
A: Paginate at the resource level, not the relationship level. If you need to list comments on a post, use /posts/{id}/comments?page=1 rather than nesting pagination inside the post object. This keeps each endpoint focused and predictable.
Q: Is it ever wrong to use flat responses? A: Flat responses become problematic when the data is genuinely hierarchical and the consumer would need to reconstruct relationships from flat lists. If you’re returning a file system structure or an organizational chart, flat responses force the consumer to do tree-building work that the API should handle. In those cases, nested responses are the right call.
Q: What about performance? Don’t nested responses save bandwidth? A: The Stack Exchange discussion notes that nested JSON can be slightly smaller even after gzip. But the difference is typically negligible for modern APIs. The cost of a slightly larger payload is almost always less than the cost of additional round trips or complex client-side data assembly. Prioritize developer experience over marginal bandwidth savings.
Q: How do I explain these decisions to stakeholders who want “the right way”? A: There is no universal right way. The LinkedIn analysis concludes that “there’s no universal rule—just context-driven design.” Frame your decisions around consumer access patterns and query frequency, not theoretical purity. Show that flat responses reduce client-side processing for your most common queries, and that nesting is reserved for cases where it demonstrably improves clarity.
Sources
- https://konghq.com/blog/enterprise/api-mandate
- https://swagger.io/blog/api-design-best-practices
- https://swagger.io/blog/code-first-vs-design-first-api
- https://www.linkedin.com/posts/tousif070_apidesign-backenddevelopment-frontenddevelopment-activity-7352736867567489024-LtkZ
- https://stackoverflow.com/questions/20951419/what-are-best-practices-for-rest-nested-resources
- https://softwareengineering.stackexchange.com/questions/350623/flat-or-nested-json-for-hierarchal-data
- https://www.moesif.com/blog/technical/api-design/REST-API-Design-Best-Practices-for-Sub-and-Nested-Resources
- https://medium.com/analytics-vidhya/database-normalization-vs-denormalization-a42d211dd891
- https://konghq.com/blog/enterprise/federated-api-management
