4

Imagine we have resources like reports. We have a collection because every report contains its own settings to build some data.

Every report calculates its values on regular basis but not to often. There is a possibility to recalculate a report just right now to have the most actual data.

The question is: what kind of request would be a most RESTfull way to do that ?

Some details after getting first answers:

This "recalculation" takes relatively a lot of time. So this cannot be a GET request because it cannot be finished more or less synchronously. And aim of the API client is not to get a result but start this refreshing process.

Another example. Imagine we have a database of people and a relatively big collection of filters every of them calculates demographics basing on some criteria. Conditions are:

  • It is very expensive to recalculate these filters
  • Only a few of filters should really deliver up-to-date calculation regularly and only client knows which ones
  • Client drives massive changes to the people's table, so only client knows when it hapens and when it makes more sense to run new calculation
5
  • How can a client possibly know what URI to call? Only the server can know if "it is time" to recalculate. If re-calc on demand really is impractical then it must be clear to users when the calc is done and the new calc must be available at a known time - which of course takes into account the time to do the calculation. And if the calc happens to be done in-between defined intervals, hold that refresh until the scheduled time. Getting all reports current in sync would greatly improve users' trust of the data. – radarbob May 17 '16 at 16:08
  • @radarbob, in real situation all reports are updated regularly but only during the first week. Then they stop because it is expensive and most of report are not needed any more. If client still wants some data due to some reason, he can refresh it by request. I will give one more example in the original question. – Stepan Stepanov May 17 '16 at 16:42
  • A better way of thinking about in keeping with REST (representational state transfer) is not that the client requests the server refreshes the report, but rather the client puts his copy of the report resource into the state of being needing to be refreshed and then tells the server, via PUT request, "hey look at this new state of the report, do what ever you need to do as a side effect". The server refreshes the data as a side effect of that state change. – Cormac Mulhall May 17 '16 at 18:37
  • @CormacMulhall, yes this is something I got in my mind, and something we've discussed in the 1st answer. In general we create some new results or recreate results we already have. The questioon is how the path should look the best way and if we have any logical conflict because client performs PUT/POST but gives no information in in, server uses its own data to build new result. – Stepan Stepanov May 18 '16 at 11:12
  • Almost correct way to trigger update on your resource just to put in headers Cache-Control: no-cache. Your presentation is just cached form of the "real state" of the report. – webaib Nov 1 '17 at 14:14
1

The question is: what kind of request would be a most RESTfull way to do that

So you have a Report resource at some URI, say mysite.com/reports/343

This resource can be in a few different states, it can be up to date, it can need to be regenerated, or it can be in the process of being regenerated

A representation of that resource might be something like this

application/vnd.stepanov_report_long-v1+json

{
  report_state: "not generating",
  report_data: [
     "John Doe": {age: 34},
     "Jane Doe": {age: 35},
     ...
     ...
     "Xan Doe": {age: 32}
 ]
}

you could also have another representation like this

application/vnd.stepanov_report_short-v1+json

{
  report_state: "not generating",
}

Most of the time the clients are interested in the full data in that report, which is one representation of the current state of that report.

But some times the clients are interested in a representation that tells the client if the report is up to date or not, or if it is being re-generated. In this case the client can request the resource (in the short form version using the Accept header). It can then update that resource and send back to the server the new version

Request

GET mysite.com/reports/343
Accept: application/vnd.stepanov_report_short-v1+json

Response from server

{
    report_state: "not generating",
}

Request back to server

PUT mysite.com/reports/343
Content-Type: application/vnd.stepanov_report_short-v1+json
{
    report_state: "generating"
}

(notice the report state has been changed by the client)

The server sees the client has put the resource into the state of regenerating and a side effect of this is that the server regenerates the actual data.

This cuts down on the need for URI scheme that contains domain specific logic. In general it is better to keep your URI scheme limited to resources and put domain specific logic into your representation format (ie your Content Type).

2
  • What about using HEAD on the URI to get the status instead of the application/vnd.stepanov_report_short-v1+json media type? I've read that some firewalls or servers don't support HEAD well but it seems to fit here. – JimmyJames Oct 12 '17 at 14:58
  • Also, would an viable alternative to PUT be to have something like a /reports/tps/ URI and POST to it? Meaning if there's a /reports/tps/343 that's old, you can POST to /reports/tps which returns e.g. /reports/tps/344. Then the old report would still be available for as long as you wish to keep it at which point you return 410. – JimmyJames Oct 12 '17 at 15:03
9

You call GET /reports/{id} with either an If-None-Match or If-Unmodified-Since header set. The server determines if it needs to recalculate or not. If not, it returns a 304 NOT MODIFIED. If so, it recalculates and returns the new report in the response body.

I should note that in the question, but recalculation takes a time. E.g. up to 10 minutes. So the request cannot be really synchronous. It is not like "I want to get the result right being up to date" but "I want this resource to become up to date right now to get it later"

In that case, make a new endpoint, such as /report-updates/{id}. Call POST /report-updates/{id}. If the report is up-to-date, return a 304 Not Modified. If the report needs to be recomputed, return a 202 Accepted. Include a response body with a current status and an estimated completion time. A GET call to /report-updates will either return 200 OK with the current status/completion time or a 307 Temporary Redirect with a Location header set to /reports/{id}.

Your other choice is to make your client poll the server on a regular basis. Use GET /reports/{id} with the conditional headers as above. Always return the 200 or 304, but kick off a recompute whenever the server data is not up to date. Then all clients are assured of getting the most recent computation.

7
  • What if it doesn't need to recalculate but the client doesn't currently have a copy of the report? – Ben Aaronson May 17 '16 at 15:08
  • 2
    I'd say then client shouldn't be posting If-None-Match nor If-Unmodified-Since, and therefore the resource should be returned with 200 OK (or whatever applicable). – Gerino May 17 '16 at 15:16
  • @BenAaronson What Gerino said. Make the conditional request whenever you can, but if you don't have an ETag or Date, make an unconditional request. – Eric Stein May 17 '16 at 15:19
  • I should note that in the question, but recalculation takes a time. E.g. up to 10 minutes. So the request cannot be really synchronous. It is not like "I want to get the result right being up to date" but "I want this resource to become up to date right now to get it later" – Stepan Stepanov May 17 '16 at 15:29
  • @StepanStepanov updated to reflect comments. – Eric Stein May 17 '16 at 16:00
1

For the reads: My usual answer is that track each "report" with several ids. For example

GET /reports/{id}/latest is going to redirect to, say,
GET /reports/{id}/latest?asOf=2016-05-17T12:00:00%20-0500

These resources always refer to reports where the processing has been completed, so there is never any waiting to be done, the representation of the report is cacheable, and so on.

For the process of actually performing an update, I'd use a different resource. As Jim Webber says, HTTP isn't your application; you use it to implement your protocol by passing documents around.

So PUT a command (or POST, if your representation of state is html) to schedule an update. It might be like:

PUT /reports/{id}/updates/your-guid-here-two-dollah

and if your application is willing to schedule an update of the report at that time, then it returns a 201 created, with a representation of application state that indicates to the client how to check for updates on the report process. At the point when the report is ready, the representation of the application state will include a link to the report that was generated.

If you've got an SLA for the report, then you can play some games with the caches to ensure that the client can "refresh" without putting additional load on your service, if you think that's important.

Recommened viewing: REST in Practice by Jim Webber -- especially when he discusses the implementation of the Restbucks protocol.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.