API REST: ¿la API debe devolver objetos JSON anidados?

38

Cuando se trata de API JSON, ¿es una buena práctica aplanar las respuestas y evitar objetos JSON anidados?

Como ejemplo, digamos que tenemos una API similar a IMDb pero para videojuegos. Hay un par de entidades, Juego, Plataforma, ESRBRating y GamePlatformMap que mapean Juegos y Plataformas.

Digamos que solicitas / game / 1 que obtiene el juego con ID 1 y devuelve el objeto del juego con las plataformas y esrbRating anidado.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"id":1,"name":"Xbox"},
    {"id":2,"name":"Playstation"}
  ],
  "esrbRating": {
    "id": 1,
    "code": "E",
    "name": "Everyone"
  }
}

Si está utilizando algo como JPA / Hibernate, puede hacerlo automáticamente si está configurado en FETCH.EAGER.

La otra opción es simplemente la API y agregar más puntos finales.

En ese caso, cuando se solicita / game / 1 solo se devuelve el objeto del juego.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
}

Si desea que las plataformas y / o ESRBRating tengan que llamar a lo siguiente:

/ juego / 1 / plataforma / juego / 1 / esrb

Parece que este método podría agregar varias llamadas más al servidor dependiendo de qué datos necesita el cliente y cuándo lo necesitan.

Hubo un último pensamiento que tuve donde tendrías que devolver algo como esto.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": ["Xbox","Playstation"]
}

Sin embargo, esto supone que no necesitan los ID o cualquier otra información que pueda estar asociada con esos objetos de la plataforma.

En general, pregunto cuál es la mejor manera de estructurar sus objetos JSON devueltos por su API. ¿Debería tratar de mantenerse lo más cerca posible de sus entidades, o está bien usar Objetos de dominio u Objetos de transferencia de datos? Entiendo que los métodos tendrán compensaciones, ya sea más trabajo en la capa de acceso a datos o más trabajo para el cliente.

También me gustaría escuchar una respuesta relacionada con el uso de Spring MVC como tecnología de back-end para la API, ya sea con JPA / Hibernate o MyBatis para la persistencia.

zorro gris
fuente
66
¿Qué objeciones, si hay alguna, tiene para devolver objetos incrustados? Devolver objetos incrustados individualmente desde diferentes puntos finales se volverá bastante molesto (sin mencionar que es lento).
Robert Harvey
1
Personalmente no tengo objeciones. Simplemente no estoy al tanto de lo que se considera mejores prácticas. Un colega afirma que trabajar con objetos incrustados en AngularJS no es sencillo y, finalmente, me gustaría que cualquiera de las aplicaciones Ember of AngularJS consuma la API. No sé lo suficiente sobre Angular o Ember para saber si eso tendrá un impacto o no.
greyfox
3
La respuesta dependerá de si desea devolver objetos de dominio, DTO, objetos ViewModel u objetos KitchenSink. Es muy probable que el objeto que está devolviendo esté determinado por lo que necesita su aplicación y cómo se comporta dicho objeto en Internet. Ejemplo: si está intentando llenar una página web con datos de una factura, es muy probable que devuelva un objeto que contenga todo lo que necesita (a menos que planee AJAXing en las líneas de pedido, o algo así).
Robert Harvey
En este caso, cuando solicita un juego, es probable que desee conocer los géneros, plataformas y ESRBRating. Eso tiene sentido. En términos de diseño desde una perspectiva Java, ¿recomendaría tener un paquete Entity que tenga entidades JPA, y luego un paquete de dominio que son los objetos comerciales / DTO que se devuelven al usuario?
greyfox
1
Las llamadas al servidor son caras. Una API que requiere que envíe datos utilizando múltiples llamadas será más lenta que una API que le permite obtener todo en una sola llamada, a menudo incluso cuando esta última devuelve información innecesaria.
Gort the Robot

Respuestas:

11

Otra alternativa (usando HATEOAS). Esto es simple, principalmente en la práctica agrega una etiqueta de enlaces en el json dependiendo de su uso de HATEOAS.

http://api.example.com/games/1:

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"_self": "http://api.example.com/games/1/platforms/53", "name": "Playstation"},
    {"_self": "http://api.example.com/games/1/platforms/34", "name": "Xbox"},
  ]
}

http://api.example.com/games/1/platforms/34:

{
  "id": 34,
  "title": "Xbox",
  "publisher": "Microsoft",
  "releaseDate": "2015-01-01",
  "testReport": "http://api.example.com/games/1/platforms/34/reports/84848.pdf",
  "forms": [
    {"type": "edit", "fields: [] },
  ]
}

Por supuesto, puede incrustar todos los datos en todos los listados, pero es probable que sean demasiados datos. De esta manera, puede incrustar los datos necesarios y luego cargar más si realmente desea trabajar con ellos.

La implementación técnica puede contener almacenamiento en caché. Puede almacenar en caché los enlaces y los nombres de las plataformas en el objeto del juego y enviarlo instantáneamente sin tener que cargar la API de la plataforma. Luego, cuando sea necesario, puede cargarlo.

Ves, por ejemplo, que agregué información de formulario. Lo hice para mostrarle que puede haber mucha más información en un objeto json detallado de lo que incluso querría cargar en la lista de juegos.

Luc Franken
fuente
No creo que sea técnicamente HATEOS ya que no hay estado.
RibaldEddie
Sí, no estoy seguro de la palabra exacta sobre este proceso. HATEOS en general se está utilizando para vincular en API de descanso, pero estoy de acuerdo en que también tiene que ver con el estado. Aunque la idea de implementación será la misma. Aquí puede ver un poco más sobre cómo se puede usar con un ejemplo: stormpath.com/blog/linking-and-resource-expansion-rest-api-tips
Luc Franken
¡Pero es una buena idea!
RibaldEddie
1
Si está desarrollando una API donde hay cohesión entre el cliente y la API en sí (digamos una API interna), podría tener más sentido devolver una respuesta anidada (o aplanada) en lugar de proporcionar enlaces a otro recurso, lo que significa más solicitudes de API que puede no ser deseado.
Bruno
@bruno sí, pero con un límite: en sistemas más grandes, no puede o no desea suministrar todos los objetos relacionados en su totalidad. Los campos que incluye de forma predeterminada son arbitrarios, puede seleccionarlos en función del uso de su API. Entonces, en este caso, es posible que tenga plataformas con cientos de campos, el caso de uso muestra un cuadro de selección para elegir una plataforma. Entonces tiene sentido incluir el nombre de la plataforma, pero no necesita los detalles financieros de la plataforma, por ejemplo.
Luc Franken
16

Esta es una de esas preguntas básicas cuando se trata del diseño de API REST. Cada diseñador se hace esta pregunta el primer día. Lo siento, pero la respuesta es "depende". Cada enfoque tiene ventajas y desventajas y solo tendrá que tomar una decisión e ir con ella.

RibaldEddie
fuente
11
Esto no es de ninguna ayuda. OP mismo sabía "depende y cada enfoque tiene pros y contras". Debe explicar de qué cosas depende o, al menos, dar algún ejemplo.
Pratik Singhal
5

Secundo el enfoque presentado aquí https://www.slideshare.net/stormpath/rest-jsonapis

En resumen, incluya el recurso anidado como enlaces en el recurso primario, mientras tanto, proporcione un parámetro de expansión en el punto final primario.

En mi opinión, esta es una forma eficiente y flexible en la mayoría de los casos.

Wei Qiu
fuente
2
Me gusta este enfoque. Para cualquiera que se pregunte, esto comienza en SLIDE 57 en la presentación de diapositivas vinculada.
Adam Plocher