Diseño de API · REST · Modelado de Datos · Arquitectura de API · Equipos Pequeños
Modelado de Datos en APIs para Equipos Pequeños: Estructuras Planas vs. Anidadas y Cómo Dejar de Normalizar en Exceso
Una guía práctica para desarrolladores independientes y equipos pequeños sobre cómo modelar recursos de API: elegir entre respuestas planas y anidadas, usar enlaces hipertextuales con criterio, manejar tipos polimórficos y evitar la trampa de la sobre-normalización.
Publicado:
La Respuesta Corta
Para la mayoría de los equipos pequeños que desarrollan APIs, comiencen con respuestas planas y desnormalizadas para operaciones CRUD simples y endpoints de listado. Reserven las estructuras anidadas para los casos donde la jerarquía es intrínseca al dominio y el frontend se beneficia genuinamente de datos agrupados. Usen enlaces hipertextuales (como links o _embedded) cuando las relaciones importan más que la duplicación. No normalicen en exceso su esquema de base de datos en la superficie de la API: su API no es su base de datos.
Esto no es simplemente una cuestión de preferencia personal; se trata de ajustar la forma de la respuesta a los patrones de acceso reales de los consumidores. Las mejores decisiones de modelado de datos para APIs provienen de observar cómo se consultarán sus endpoints, no de la pureza teórica.
Por Qué el Modelado de Recursos Importa Más para Equipos Pequeños
Cuando eres un equipo de dos o tres desarrolladores que lanza una API de la que otras personas dependen, cada decisión de modelado tiene un peso desproporcionado. A diferencia de las grandes organizaciones con equipos dedicados de productos API, los equipos pequeños no tienen el lujo de iterar sobre un modelo de datos mal diseñado a través de múltiples versiones principales. Un mal modelo de recursos desde el inicio se convierte en una deuda que se acumula con cada nuevo consumidor.
El principio API-first, famoso por el mandato de Amazon alrededor de 2002, requiere que todos los equipos expongan sus datos y funcionalidades a través de interfaces de servicio. Pero el mandato no dice nada sobre cómo estructurar los datos dentro de esas interfaces. Ahí es donde los equipos pequeños suelen tropezar: confundiendo la normalización de la base de datos con el diseño de la API.
Como señala la documentación de Swagger sobre mejores prácticas REST, los recursos son fundamentales para REST. Un recurso es un objeto lo suficientemente importante como para ser referenciado por sí mismo, con datos, relaciones y métodos que operan sobre él. Un grupo de recursos se llama colección. La forma en que exponen estos a través de URLs y cuerpos de respuesta es donde ocurre el verdadero trabajo de diseño.
Respuestas Planas: Cuándo la Simplicidad Gana
Una estructura de respuesta plana (desnormalizada) coloca todos los campos relevantes en el nivel superior del objeto JSON. Consideren un endpoint de cliente:
{
"id": 5,
"first_name": "Cole",
"last_name": "Palmer",
"email": "cole@gmail.com",
"phone": "880147258369",
"business_name": "Palmer Leather & Shoes",
"website": "palmer-leather.com"
}
Este enfoque tiene ventajas claras para equipos pequeños:
- Accesos superficiales. Los consumidores leen
customer.emailen lugar de navegar porcustomer.contact.email. Para endpoints de listado y tablas de datos, esto reduce el procesamiento del lado del cliente. - Paginación y filtrado más sencillos. Cuando cada campo por el que podrían filtrar se encuentra en el nivel superior, pueden construir parámetros de consulta sin preocuparse por la traversión de objetos anidados.
- Serialización predecible. No existe riesgo de que un consumidor reciba accidentalmente un objeto profundamente anidado cuando esperaba un registro plano.
El análisis sobre estructuras planas versus anidadas en LinkedIn señala que las respuestas planas son “ideales para endpoints CRUD simples y tablas de datos” y que muchos desarrolladores backend prefieren personalmente este enfoque para “la mayoría de las APIs internas y sencillas.”
Usen respuestas planas cuando:
- Estén construyendo APIs internas o operaciones CRUD sencillas
- El mismo recurso aparezca en múltiples contextos con formas ligeramente diferentes
- Sus consumidores necesiten principalmente listar, filtrar y ordenar datos
- La velocidad de ancho de banda y análisis sea más importante que la elegancia estructural
Respuestas Anidadas: Cuándo la Jerarquía Aporta Valor
Una estructura de respuesta anidada (jerárquica) agrupa campos relacionados en objetos lógicos:
{
"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"
}
}
Las estructuras anidadas cumplen una función. La guía de Moesif sobre recursos anidados en REST explica que URLs anidados como /posts/:postId/comments/:commentId transmiten una relación jerárquica que URLs planos como /comments/:commentId no pueden. Esto importa para la legibilidad y la depuración: cuando un consumidor de API ve una ruta anidada, entiende inmediatamente la propiedad y el contexto.
La discusión en Stack Exchange sobre JSON plano versus anidado para datos jerárquicos destaca que las estructuras anidadas están “ya en un formato utilizable” y “ahorran algo de ancho de banda” incluso después de la compresión gzip. Para aplicaciones con modelos de dominio ricos donde la jerarquía refleja relaciones del mundo real, el anidamiento reduce la carga cognitiva en los consumidores que ya piensan en términos de objetos y componentes.
Usen respuestas anidadas cuando:
- Su modelo de dominio es inherentemente jerárquico (organizaciones con departamentos, carpetas con archivos)
- El agrupamiento mejora la claridad semántica y el frontend renderiza los datos en componentes correspondientes
- Estén construyendo una API pública donde la estructura comunica límites del dominio
- El mismo objeto anidado aparezca de manera consistente en múltiples endpoints
El Debate sobre URLs Anidados
Uno de los puntos de confusión más comunes para equipos pequeños es si deben usar URLs anidados. La discusión en Stack Overflow sobre recursos anidados en REST ilustra esto perfectamente. Consideren una jerarquía donde las empresas poseen departamentos y los departamentos poseen empleados:
/companies/{companyId}/departments/{departmentId}/employees/{empId}
El problema surge cuando necesitan listar todos los empleados a través de todas las empresas. Un diseño puramente anidado los fuerza a soluciones alternativas incómodas. El artículo de Moesif señala que los recursos anidados pueden crear una “aparencia de relación jerárquica” incluso cuando el modelo de datos subyacente es muchos-a-muchos. La API de GitHub, por ejemplo, expone tanto /users/:userName/repos como /repos/:repoName/users para representar una relación muchos-a-muchos desde ambas direcciones.
Aquí está la guía práctica para equipos pequeños:
-
Den a cada recurso una ruta canónica. Un empleado debe ser direccionable en
/employees/{id}independientemente de a qué empresa pertenezca. Los URLs anidados son útiles para acotar consultas, pero no deben ser la única forma de acceder a un recurso. -
Usen URLs anidados para contexto, no para propiedad.
/companies/{id}/departmentses útil cuando listan departamentos dentro de una empresa específica. Pero/departments/{id}también debería funcionar cuando necesitan un departamento de forma aislada. -
Eviten el anidamiento profundo más allá de dos niveles. Como señala el hilo de Stack Overflow, rutas como
/companies/{companyId}/departments/{departmentId}/employees/{empId}se vuelven engorrosas rápidamente. Si descubren que están anidando tres o cuatro niveles, probablemente están modelando la abstracción incorrecta.
Enlaces Hipertextuales: La Tercera Opción
Ni lo plano ni lo anidado son siempre la respuesta. A veces la mejor aproximación es mantener los recursos planos en el nivel superior y usar enlaces hipertextuales para expresar relaciones. Este es el enfoque recomendado por la especificación HAL (Hypertext Application Language) y utilizado por APIs como la de GitHub.
{
"id": 5,
"name": "Cole Palmer",
"email": "cole@gmail.com",
"links": {
"company": "/companies/42",
"department": "/departments/7"
}
}
Este enfoque les da la simplicidad de las respuestas planas con la flexibilidad de las relaciones explícitas. Los consumidores pueden seguir enlaces cuando necesitan datos relacionados, pero no están obligados a analizar objetos anidados que no necesitan.
La idea clave del artículo sobre normalización versus desnormalización de bases de datos es que su API no debe reflejar su esquema de base de datos. Una base de datos normalizada con claves foráneas es excelente para la integridad de datos. Pero una API que expone esas mismas claves foráneas como búsquedas anidadas fuerza a cada consumidor a realizar solicitudes adicionales o a analizar estructura innecesaria.
Los enlaces hipertextuales les permiten servir datos desnormalizados en la superficie de la API mientras preservan la estructura normalizada en su base de datos. Este es el punto dulce para equipos pequeños: su base de datos permanece limpia y su API permanece sencilla.
Tipos Polimórficos: No los Oculten Detrás del Anidamiento
Los equipos pequeños frecuentemente se encuentran con relaciones polimórficas, donde un solo campo puede referenciar diferentes tipos de recursos. Un ejemplo común es un sistema de notificaciones donde una notificación puede referenciar tanto un pedido como un mensaje.
La tentación es anidar tipos polimórficos dentro de un solo objeto:
{
"id": 101,
"type": "order",
"related": {
"order_id": 42,
"total": 150.00,
"status": "shipped"
}
}
Esto es una trampa de modelado. El objeto related anidado cambia de forma dependiendo del campo type, lo que hace que el análisis del lado del cliente sea frágil y la documentación dolorosa. En su lugar, usen una estructura plana consistente con un discriminador de tipo:
{
"id": 101,
"type": "order",
"related_id": 42,
"related_type": "order"
}
O mejor aún, usen endpoints separados y dejen que el consumidor solicite el recurso que necesita:
GET /notifications/101
GET /orders/42
La guía de mejores prácticas de Swagger enfatiza que las URLs deben ser “limpias, elegantes y simples para que los desarrolladores que usan su producto puedan usarlas fácilmente.” El anidamiento polimórfico viola ese principio al hacer que la forma de la respuesta sea impredecible.
La Trampa de la Sobre-Normalización
El error más común que cometen los equipos pequeños de APIs es sobre-normalizar sus estructuras de respuesta. Esto usualmente proviene de uno de dos hábitos:
Hábito 1: Reflejar la base de datos. Si su base de datos tiene una tabla users, una tabla profiles y una tabla settings, podrían sentirse tentados a exponerlas como tres objetos anidados. Pero los consumidores de su API no les importa su estructura de tablas. Les importa qué datos necesitan para una operación dada. El artículo sobre normalización versus desnormalización deja claro que la desnormalización en la capa de API es a menudo la elección correcta, incluso si eso significa duplicar algunos campos.
Hábito 2: Abstracción prematura. Los equipos pequeños a veces diseñan APIs para un futuro que podría nunca llegar. Crean estructuras profundamente anidadas “por si acaso” el frontend necesita agrupar datos de forma diferente más tarde. Esto añade complejidad por cero beneficio inmediato. El artículo sobre code-first versus design-first de Swagger señala que un enfoque code-first puede convenir a “prototipado rápido, equipos pequeños o proyectos con desarrollo altamente iterativo” precisamente porque evita la sobre-ingeniería. Comiencen sencillo. Añadan anidamiento solo cuando un patrón de consumidor lo demande.
Una prueba práctica para la sobre-normalización: si su respuesta de API requiere que el consumidor realice tres o más solicitudes de seguimiento para ensamblar una sola vista, han sobre-normalizado. Aplanen la respuesta o añadan un endpoint dedicado que devuelva la vista ensamblada.
Pasos Concretos para el Diseño de su Próxima API
-
Mapeen sus patrones de acceso de consumidores primero. Antes de escribir un solo endpoint, listén las diez consultas principales que recibirá su API. Si ocho de ellas son “dame una lista de X con los campos A, B y C,” comiencen con respuestas planas.
-
Comiencen planos, aniden solo cuando esté justificado. Diseñen su primera versión con respuestas planas. Introduzcan el anidamiento solo cuando tengan una solicitud concreta de consumidor que las estructuras planas no puedan servir eficientemente.
-
Usen hipervínculos para relaciones, no anidamiento para todo. Si el recurso A referencia al recurso B, añadan un enlace. No incrusten B dentro de A a menos que la incrustación sea genuinamente útil para cada consumidor.
-
Den a los recursos rutas canónicas. Cada recurso debe ser direccionable en su propia URL de nivel superior, independientemente de cómo se acote en consultas anidadas.
-
Documenten la forma, no el esquema. Su especificación OpenAPI debe describir lo que obtienen los consumidores, no lo que contiene su base de datos. El enfoque design-first de Swagger, crear una definición de API detallada antes de escribir código, ayuda a mantener clara esta distinción.
-
Prueben con consumidores reales temprano. El artículo sobre gestión de APIs federadas de Kong enfatiza que las plataformas API deben “elevar los estándares de ingeniería y construir APIs de mayor calidad.” La mejor forma de validar sus decisiones de modelado es tener consumidores reales usando la API antes de comprometerse con una estructura.
FAQ
P: ¿Debo usar URLs anidados si mi base de datos tiene claves foráneas? R: No. Las claves foráneas son una preocupación de la base de datos. Usen URLs anidados para acotar y legibilidad, pero siempre proporcionen una ruta canónica de nivel superior para cada recurso. El artículo de Moesif advierte que los URLs anidados pueden crear una “aparencia engañosa de relación jerárquica” cuando el modelo de datos subyacente es más complejo.
P: ¿Cómo manejo la paginación con recursos anidados?
R: Paginen a nivel de recurso, no a nivel de relación. Si necesitan listar comentarios en una publicación, usen /posts/{id}/comments?page=1 en lugar de anidar la paginación dentro del objeto de la publicación. Esto mantiene cada endpoint enfocado y predecible.
P: ¿Es incorrecto usar respuestas planas en algún caso? R: Las respuestas planas se vuelven problemáticas cuando los datos son genuinamente jerárquicos y el consumidor necesitaría reconstruir relaciones a partir de listas planas. Si están devolviendo una estructura de sistema de archivos o un organigrama, las respuestas planas fuerzan al consumidor a hacer trabajo de construcción de árboles que la API debería manejar. En esos casos, las respuestas anidadas son la opción correcta.
P: ¿Qué pasa con el rendimiento? ¿Las respuestas anidadas ahorran ancho de banda? R: La discusión en Stack Exchange señala que el JSON anidado puede ser ligeramente más pequeño incluso después de gzip. Pero la diferencia es típicamente insignificante para APIs modernas. El costo de un payload ligeramente más grande casi siempre es menor que el costo de rondas adicionales o del ensamblaje complejo de datos del lado del cliente. Prioricen la experiencia del desarrollador sobre ahorros marginales de ancho de banda.
P: ¿Cómo explico estas decisiones a stakeholders que quieren “la forma correcta”? R: No existe una forma universalmente correcta. El análisis en LinkedIn concluye que “no hay regla universal, solo diseño basado en contexto.” Enmarquen sus decisiones alrededor de los patrones de acceso de los consumidores y la frecuencia de consultas, no alrededor de la pureza teórica. Muestren que las respuestas planas reducen el procesamiento del lado del cliente para sus consultas más comunes, y que el anidamiento se reserva para casos donde mejora demostrablemente la claridad.
Fuentes
- 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
