¿Puede una API REST devolver múltiples recursos como un único recurso compuesto?

10

Estoy en el proceso de crear una API REST y actualmente encuentro el siguiente problema:

  • FooEs el primer recurso. Las operaciones CRUD se pueden aplicar a través del /foo/URI.
  • BarEs el segundo recurso. Las operaciones CRUD se pueden aplicar a través del /bar/URI.
  • Todos Fooestán asociados con cero o uno Bar. La razón por la que no trato Barcomo un subrecurso Fooes porque la misma Barinstancia se puede compartir entre varios Foo. Así que pensé que es mejor acceder a él a través de un URI independiente en lugar de hacerlo /foo/[id]/bar.

Mi problema es que en una cantidad significativa de casos, los clientes que solicitan una Fooinstancia también están interesados ​​en la Barinstancia asociada . Actualmente, esto significa que tienen que realizar dos consultas en lugar de una. Quiero presentar una forma que permita obtener ambos objetos con una sola consulta, pero no sé cómo modelar la API para hacerlo. Lo que se me ocurrió hasta ahora:

  • Podría introducir un parámetro de consulta similar a esto: /foo/[id]?include_bar=true. El problema con este enfoque es que la representación de recursos (p. Ej., La estructura JSON) de la respuesta necesitaría verse diferente (p. Ej., Un contenedor como en { foo: ..., bar: ... }lugar de solo un serializado Foo), lo que hace que el Foopunto final del recurso sea "heterogéneo". No creo que sea algo bueno. Al realizar consultas /foo, los clientes siempre deben obtener la misma representación de recursos (estructura), independientemente de los parámetros de consulta.
  • Otra idea es introducir un nuevo punto final de solo lectura, por ejemplo /fooandbar/[foo-id]. En este caso, no es problema devolver una representación como { foo: ..., bar: ... }, porque entonces es solo la representación "oficial" del fooandbarrecurso. Sin embargo, no sé si ese punto final auxiliar es realmente RESTful (es por eso que escribí "can" en el título de la pregunta. Por supuesto, es técnicamente posible, pero no sé si es una buena idea).

¿Qué piensas? existen algunas otras posibilidades?

ceran
fuente
¿Cuál es el término para la relación entre Foo y Bar? ¿Podría decir que Bar es padre de Foo?
Nathan Merrill
A Barno puede existir sin estar asociado a a Foo. Sin embargo, como escribí anteriormente, es posible que múltiples Foocorreos compartan lo mismo Bar. Debería ser posible crear un Foosin un Barasociado, por lo que no creo Barque deba tratarse como padre.
ceran
1
Creo que está experimentando algunos de los problemas que he tenido al traducir directamente las relaciones del modelo de dominio en URI y equiparar recursos a entidades de dominio . Puede interesar que las API REST deben estar impulsadas por hipertexto . Atención especial al 4to punto
Laiv

Respuestas:

6

Una API REST de nivel 3 le devolverá un Fooy también un enlace que indica lo relacionado Bar.

GET /foo/123
<foo id="123">
  ..foo stuff..
  <link rel="bar" uri="/bar/456"/>
</foo>

Luego puede agregar una función de "profundizar" a su API que permite la navegación de enlaces;

GET /foo/123?drilldown=bar
<foo id="123">
  ..foo stuff..
  <link rel="bar" uri="/bar/456">
    <bar id="456">
      ..bar stuff...
    </bar>
  </link>
</foo>

La función de profundización se ubicaría frente a las API e interceptaría las respuestas. Haría las llamadas de profundización y completaría los detalles antes de devolver la respuesta a la persona que llama.

Esto es algo bastante común en REST de nivel 3, ya que reduce mucho el chat del cliente / servidor sobre http lento. La compañía para la que trabajo produce una API REST de nivel 3 con exactamente esta característica.

Actualización: para lo que vale, así es como podría verse en JSON. Así es como lo estructuraría nuestra API. Tenga en cuenta que puede anidar sus desgloses para extraer enlaces de enlaces, etc.

GET /foo/123?drilldown=bar

{
  "self": {
    "type": "thing.foo",
    "uri": "/foo/123=?drilldown=bar",
    "href": "http://localhost/api/foo/123?drilldown=bar"
  },
  "links": [
    {
      "rel": "bar",
      "rev": "foo",
      "type": "thing.bar",
      "uri": "/bar/456",
      "href": "http://localhost/api/bar/456"
    }
  ],
  "_bar": [
    {
      "self": {
        "type": "thing.bar",
        "uri": "/bar/456",
        "href": "http://localhost/api/bar/456"
      },
      "links": [
        {
          ..other link..
        },
        {
          ..other link..
        }
      ]
    }
  ]
}
Qwerky
fuente
Interesante, ya estoy usando enlaces / controles hipermedia para eliminar el acoplamiento estrecho a los URI, pero no he pensado en la idea de "profundizar" que parece muy prometedora. ¿Cómo podría ser una representación JSON? En este momento, cada representación JSON de mis recursos contiene una linksmatriz, cada entrada es un objeto de enlace con un rely un uricampo (similar a su ejemplo xml). ¿Debo agregar un tercer campo a cada objeto de enlace (por ejemplo data)? ¿Hay algún estándar?
ceran
El desglose no es realmente una característica de descanso, por lo que no hay estándares (al menos que yo sepa).
Qwerky
Hay algunos estándares propuestos, como stateless.co/hal_specification.html que estoy usando en nuestras aplicaciones. Está muy cerca de tu ejemplo.
Pete Kirkham el
4

Si el 95% de todas las consultas quieren Foo, así como Bar, a continuación, sólo tiene que devolverlo dentro del Fooobjeto cuando se solicita una Foo. Simplemente agregue una propiedad bar(o algún otro término para la relación) y coloque el Barobjeto allí. Si la relación no existe, utilice nulo.

Creo que estás pensando demasiado en esto :)

Nathan Merrill
fuente
No debería haber sacado ese número (95%), fue un error, lo siento. Lo que quería decir era que una gran parte de las solicitudes está interesada en ambos recursos al mismo tiempo. Pero todavía hay una cantidad relevante de solicitudes que solo están interesadas Foo, y dado que cada una Bares bastante grande en la memoria (alrededor de 3x-4x del tamaño de Foo), no quiero devolver un mensaje Barsi un cliente no lo solicita explícitamente.
ceran
¿Qué tan grandes estamos hablando? Dudo que va a hacer que gran parte de la diferencia en el tiempo de transferencia, y yo prefiero una API limpia sobre la velocidad
Nathan Merrill