I'm working on a product that exposes a RESTful API to interact with system entities (the usual CRUD), which works very well.
In addition to CRUD, we've recently encountered a need to expose the ability to query different time series based data, with different filters and at varying aggregation resolutions (but based on pretty much the same aggregate functions).
For example, consider a system that manages wind turbines. You would have usual CRUD operations to add / remove turbines, but also some kind of API to query the turbines electrical output or the rotation speed over time. You might want to get these metrics averaged out across all turbines in a site, or for a single turbine, or for all turbines from a specific maker (these are your "filters"). You might want the raw data (that is every measurement done in a given time period), or hourly data points, or daily or weekly ones (this is your granularity or resolution).
You can safely assume that all queries have a start / end time filters and that sorting is always based on time.
The main consumer of this data is a GUI that renders charts based on it. It is helpful, although not strictly required, that multiple metrics are encapsulated in a single endpoint (that is you can get both the turbine's rotation speed and output for a point in time with a single query).
What we've done historically is consider such a data point as a REST entity (say we call it TurbineStats
), and have an endpoint that only supported listing entities, and accept filters & resolution as query parameters. For example:
GET /turbine/stats ⏎
?start=2018-01-01T10:11:12 ⏎
&end=2018-01-01T10:11:12 ⏎
&site_id=1234abcd ⏎
&model=ABCXYZ ⏎
&resolution=hourly
[
{
"timestamp": "2018-01-01T11:00:00",
"rotation_speed": 523.53,
"output_kwh": 20.5
},
{
"timestamp": "2018-01-01T12:00:00",
"rotation_speed": 433.13,
"output_kwh": 18.5
},
{
"timestamp": "2018-01-01T13:00:00",
"rotation_speed": 569.0,
"output_kwh": 24.1
},
...
]
This works, but feels to us more and more like an anti-pattern, mostly because:
- While entities look more or less the same and have the same fields, the data can have different meaning depending on the query. For example
rotation_speed
can be exact or averaged. - There's important meta-data (like what are the filters, how many points were considered, how many turbines are included, etc.) which can be included as part of the entities but mixing data and metadata feels awkward.
- Requesting a single entity makes little sense. This is basically a "list only" API
- Entities are computed and do not have unique identifiers. We can come up with computed identifier but it feels like we're doing that just to be "RESTful" and not for a real use.
Can you give an example of time series querying API that works well and feels well-designed, RESTful or not? Are there any good patterns to look at?
query=
parameter, where as in my case there is a lot of defined domain boundaries and I can be less flexible and more explicit about endpoints and parameters.