Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Representing non-resourceful aggregated data with JSON API

I'm working on an app for which we're studying the possibility of using jsonapi to describe the data in all API responses. It works pretty well for resource-like entities, but we're having some trouble coming up with a way to describe reports data in a jsonapi-like way.

By reports data I refer to data aggregated and computed on the fly from the base resource-like data we store in our database. For instance, imagine we store real estate information, and we have information about houses, apartments and office space, each associated to location, size of the property in square feet, type of property (wether it's a house, apartment or office space), and any other relevant information about a property. Now imagine we need a report that receives ?group_by[]=location&group_by[]=type and we want the response to convey aggregated information about the intersection of those two group_by parameters. So we would receive for instance, an object containing the average square feet area of all properties in a given location, grouped also by type of property.

Average Property Sizes (in square feet)
                Houses    Apartments   Offices
Manhattan      1234.56        234.56    123.45
Cape Coral      456.78        654.32    876.54
Portland       4321.00        987.65   2345.67

The most resource-like thing we can think of from this data is each cell, but since they are the result of a computed aggregation of more basic data, they do not have a natural ID. We've been thinking about delivering them with a computed ID as well (perhaps combining the IDs of the dimensions by which their data is grouped, i.e. "house,34" where house represents a type of property, and 34 is the ID of the location "Manhattan"). Then each cell would have the relationship with the corresponding location record, which would be included in the included section of the payload. Here's a sample json file of how would this look like:

{
  "data": [
    {
      "id": "house,123",
      "type": "report_items",
      "attributes": {
        "property_type": "house",
        "value": 108.75
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 123
          }
        }
      }
    },
    {
      "id": "house,124",
      "type": "report_items",
      "attributes": {
        "property_type": "house",
        "value": 36.0
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 124
          }
        }
      }
    },
    {
      "id": "house,125",
      "type": "report_items",
      "attributes": {
        "property_type": "house",
        "value": 1.0
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 125
          }
        }
      }
    },
    {
      "id": "office,123",
      "type": "report_items",
      "attributes": {
        "property_type": "office",
        "value": 4.0
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 123
          }
        }
      }
    },
    {
      "id": "office,124",
      "type": "report_items",
      "attributes": {
        "property_type": "office",
        "value": 2.0
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 124
          }
        }
      }
    },
    {
      "id": "apartment,123",
      "type": "report_items",
      "attributes": {
        "property_type": "apartment",
        "value": 2.0
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 123
          }
        }
      }
    },
    {
      "id": "apartment,125",
      "type": "report_items",
      "attributes": {
        "property_type": "apartment",
        "value": 4.5
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 125
          }
        }
      }
    },
    {
      "id": "apartment,124",
      "type": "report_items",
      "attributes": {
        "property_type": "apartment",
        "value": 2.0
      },
      "relationships": {
        "location": {
          "data": {
            "type": "locations",
            "id": 124
          }
        }
      }
    }
  ],
  "included": [
    {
      "type": "locations",
      "id": "123",
      "attributes": {
        "name": "Manhattan"
      }
    },
    {
      "type": "locations",
      "id": "124",
      "attributes": {
        "name": "Cape Coral"
      }
    },
    {
      "type": "locations",
      "id": "125",
      "attributes": {
        "name": "Portland"
      }
    }
  ]
}

My question is: is this a proper way of representing this kind of data in jsonapi? Is jsonapi suitable and/or recommended for data that does not directly map to resources? Would I be better off representing this data in custom json? I know non of these questions probably have a definite answer, but perhaps there's already some experience out there on how to approach similar scenarios, pros and cons of trying to make this kind of data fit onto jsonapi, etc. Any comments and help is very much appreciated. Thanks.

PS: I posted this even after some digging in the forum and on the internet, and these are the only two links that I found that talk about something that resembles what I'm trying to find out, and I include them here for references as well: 1.- http://discuss.jsonapi.org/t/composite-id-inside-the-resource-object/367/13 2.- http://discuss.jsonapi.org/t/extension-for-chart-graph-data/408

like image 567
Ernesto Avatar asked Apr 18 '16 21:04

Ernesto


1 Answers

The general answer is to consider which data is significant enough to warrant an identity on both sides of your API. By this I mean to decide which things you either want to reference later or represent with relationships. JSON API lets you define those things as resources and allows you to mix resources with more generic JSON for data that is opaque.

For instance, maybe reports and the options and filters that you used to create them are worth tracking so that a client can request a fresh viewing of the same report by its id. Maybe you want to poll your server to see which reports are being created.

On the client-side, you may want to present links from property_type resources to more information about those property types.

Or perhaps the results in a report are better represented as a blob of JSON within a resource. attributes and meta can contain any type of JSON values.

In your particular case, your primary resource could be of type reports, or an array of report_items, or perhaps even an array of property_summaries with relationships to property_types and locations.

If you choose more generic types of resources, you can generalize the reporting process, but you might not capture the significance of the data.

If you choose very specific resources for reporting, you'll need to really customize each type of report, but you'll be able to make meaningful connections among your resources on your client.

like image 168
Dan Gebhardt Avatar answered Oct 26 '22 00:10

Dan Gebhardt