My first proposal would be to have the server update the user object every X minutes as a background process. I don't see any reason to place the burden of keeping server data up-to-date on the client. Responses to the GET
call would include an Expires header. The client could then cache the response for a fixed amount of time, saving you server hits until the data gets refreshed.
If you must make the refresh be client-driven, you want your GET
to return a 202 Accepted
, which indicates a valid request that the API is working on but has not completed. The entity that gets returned from your GET
request should provide a timestamp for when the API should check back to get the updated data. Once the data has been refreshed, the GET
will return a 200 Ok
with the refreshed data. This is the approach I recommend.
GET /userObject
<- 202 Accepted
{ "checkAt": <timestamp> }
GET /userObject
<- 200 OK
{ "userName": "Bob", ... }
You could also consider using the Retry-After
header in your response, but that's only appropriate for 503 Service Unavailable
or any of the various 3xx (Redirection)
responses. You definitely aren't describing a 503
, and it doesn't sound like redirection is correct either.
If you do want to go the redirection route, you'd return a 302 Found
, specifying the temporary URI in the Location
header and the delay time in the Retry-After
header.
A fourth approach would be to use a POST
and the Post-Redirect-Get pattern. You could POST
to your userObject URI and have it return the 302 Found
with the Retry-After
header.
I really don't think that options three or four buy you anything that the second option doesn't, and I think it's the most clear. Three implies that your resource currently lives in a different location when it doesn't. Four transforms what is fundamentally a GET
request (give me the user object) into a POST
(refresh the user object, but only if you need to).
If you do decide to follow @JonSkeet's suggestion, you probably want a separate resource, something like /userObjects
and /userObjectRequests
. The client would always POST
to /userObjectRequests
. If the userObject was valid on the back end, that POST
would return a 302
to /userObjects
. If it wasn't valid, the POST
would return an entity with an id and an estimated completion time. The client could call GET
on /userObjectRequests/{id}
, and they'd either get a 302
to the userObject (if it's ready) or a 200
with the id and a new estimated completion time.