Reputation: 2020
We have a FitnessClassesService
which allows scheduling fitness-training classes. There are a number of actors for every class:
When a class is created, all the actors are also added to it. The trainers have an app from where they can see the classes which they have to participate in today (in any role).
I create classes by calling POST /classes
.
What would be the correct REST API for getting all classes for a trainer when they open their app. These are the alternatives I considered:
Get /classes
- Get userId from the HTTP header and use that to get classes for the current logged-in user only. However, this does not seem very RESTful to me.GET /classes/~alice
or Get /classes/current
- From designing-uri-for-current-logged-in-user-in-rest-applications . This will only get classes represented by the "current" user. However, unlike the example in the linked question where "users" was a resource and the "current" user represented a specific resource, I don't feel like "current" represents a resource for my usecase. "current" for me represents all classes where I am an interested party. This sounds like I have to filter
on the classes
resource instead of asking for a specific resource.GET /classes?actorId=alice
or GET /classes?actorId=current
- But what if someone calls GET /classes
. Should I verify that an actorId must always be passed. Additionally, the actorId must match the logged-in user's id. Is it okay to do such authorization based on URI parameters.Get /myclasses
- Use a different URI. This means that I will be creating classes by POST /classes
but getting classes by a different URI.What would be the canonical way of handling this.
Upvotes: 1
Views: 1201
Reputation: 57239
What would be the correct REST API for getting all classes for a trainer when they open their app
If you want a REST API, an early step is to think carefully about your resources.
Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author's hypertext reference must fit within the definition of a resource.
Conceptually, "Alice's schedule" and "Bob's schedule" are resources. So too is "MY schedule", but of course we have a pronoun antecedent problem.
There are two pretty straight forward ways to handle an authenticated request for "my schedule"; you can respond by redirecting to the correct resource, and you can inline the current representation of the correct resource into your response (with metadata indicating where it came from). Those approaches are both fine.
(Note: RFC 7234 constrains the caching of authenticated responses, which is part of why it is "fine").
REST doesn't care what spellings you use for your URI (so long as it conforms to the production rules defined in RFC 3986). So all of these are fine
/classes/actorId=alice
/classes?actorId=alice
/classes/alice
/classes?alice
Use a different URI.
Still fine.
One of the things that you need to be aware of is that cache invalidation is tied to the URI; when you POST to /classes
, it will invalidate your locally cached representation of /classes
but not your locally cached representation of /myclasses
. Which means that when you have the same information encoded into the representations of multiple resources, those representations will not necessarily be synchronized.
I PUT to /classes/{classId} , will it invalidate the cached response associated with /classes. Similarly, if I POST to /classes, will it invalidate the cached response associated with /classes/alice
No to both. The hierarchy implied by similar identifiers is not real. You describe relationships between URI via link relations, not spelling, and there aren't currently standardized link relations that mean "these resources should be invalidated together".
When I PUT /classes/{classId}, shouldn't the cache for GET /classes be invalidated. Otherwise GET /classes may return a stale representation of /classes/{classId}
As far as general purpose components are concerned, /classes/{classId}
is completely unrelated to /classes
. Identifiers are identifiers, not "identifiers and also implicit link relations".
The general purpose mechanism for describing a relationship between links is a link relation. So if we wanted to announce to general purpose components that the changes to this resource also invalidate cached representations of that resource, we would need something like Linked Cache Invalidation.
But... that draft expired seven years ago without being adopted, and I haven't yet found a substitute for it.
Which in essence means that the limitations on cache invalidation are a constraint that you need to keep in mind when designing your resource model. If it's important that information A and information B get invalidated together, then they need to be modeled by the same resource.
Of course, you can including caching information in your payloads, so that custom clients can know that the data is invalid; you express the link relation in your record schema, and clients that are aware of the schema can choose the appropriate action. You can even lift that information from the body of the message to the headers, if you like. But since none of the standard components know what your new header means, they all just ignore it (in other words, its just noise until we have a contract that both producers and consumers understand).
Upvotes: 1