/competitions/1/clubs/5/players
/players/search?club_id=5
/players?club_id=5
When should I use a first-class URL for a resource, and when should I use a nested URL?
Update 1
Thanks for the answers so far. I'll try to clarify things a little further.
Competition and Club have a many-to-many relationship. Clubs can participate in multiple competitions. I guess that would make Club a first class entity, so the way to access a club would be for instance:
/clubs/33
But I also need to be able to access clubs that participate in a specific competition, so I need something like this too:
/competitions/2/clubs
But someone mentioned it isn't recommendable to make a resource accessible via multiple URI's. Doesn't this violate that?
Also, I presume a URI like this would not be preferable:
/competitions/2/clubs/33/players/5
But rather use this:
/clubs/33/players/5
Club has a one-to-many relationship with Player.
/competitions/1/clubs/5/players
As a URI is the identifier of a single resource, I would say the general rule is that if it is an object, it gets a 'first-class URL'.
I only tend to use the query parameters only when limiting/filtering lists, for example, /competitions/1/clubs/5/players?gender=MALE.
I use path elements if the relation "feels" tree/directory wise (like club has players /clubs/berlin/players). Parameters are more "tags", I use it often for search-filters (e.g. defenders of 'berlin' club with age older as 22 /clubs/berlin/players?position=defender&age=22).
I design URL structure by 'domain-importance'. The most basic concepts should go to the root. If possible don't go too deep down url-structure, I try not to duplicate or create alias collections which represent identical resources (costs double maintenance in code + documentation).
Generally putting /clubs as root feels more natural: /clubs/{club_id}/players
I would only expose players through /competitions/{comp_id}/clubs/{club_id}/players, if players-set of is different as /clubs/{club_id}/players, e.g. during competition
several players are blocked or didn't make it for the match-squad.
What do you mean with /competitions? Is it a tournament or a single match? If single match with two clubs maybe use home + away domain-concepts: /competitions/{comp_id}/home-club and
/competitions/{comp_id}/away-club .
Update-1 Answer
Here my thoughts on your update-question:
I guess /competitions/2/clubs is a subset of /clubs, not every club is competing in every competition. So both resources are different, so two URLs are fine.
Thinking again /competitions/2/clubs/33/players/5 should also be fine (but it is important that in server code duplication is avoided). This URL should even be mandatory when the returned resource is a subset of /clubs/33/players (e.g. players are injured or limit of team-size has been hit for specific competition).
I wouldn't put the ID numbers in the URL. They mean something only for those who actually knows what they mean, but for everyone else they are meaningless numbers.
You should always choose descriptive and related words for your URL, because the URL contribute to give informations about the linked resource.
Instead of using meaningless ID numbers, choose a unique name representing the name of the team or the competition, for example
/competitions/worldcup/clubs/usa/players
But if you really need to send that kind of anonymous data in the URL, then I would prefer to see them in a query.
Use only meaningful text for the URL.
Related
The title is probably poorly worded, but I'm trying my hand at creating a REST api with symfony. I've studied a few public api's to get a feel for it, and a common principle seems to be dealing with a single resource path at a time. However, the data I'm working with has a lot of levels (7-8), and each level is only guaranteed to be unique under its parent (the whole path makes a composite key).
In this structure, I'd like to get all children resources from all or several parents. I know about filtering data using the queryParam at the end of a URI, but it seems like specifying the parent id(s) as an array is better.
As an example, let's say I have companies in my database, which own routers, which delegate traffic for some number of devices. The REST URI to get all devices for a router might look like this:
/devices/company/:c_id/routers/:r_id/getdevices
but then the user has to crawl through all :r_id's to get all the devices for a company. Some suggestions I've seen all involve moving the :r_id out of the path and using it in the the query string:
/devices/company/:c_id/getdevices?router_id[]=1&router_id[]=2
I get it, but I wouldn't want to use it at that point.
Instead, what seems functionally better, yet philosophically questionable, is doing this:
/devices/company/:c_id/routers/:[r_ids]/getdevices
Where [r_ids] is a stringified array of ids that can be decoded into an array of integers/strings server-side. This also frees up the query-parameter string to focus on filtering devices by attributes (age, price, total traffic, status).
However, I am new to all of this and having trouble finding what is "standard". Is this a reasonable solution?
I'll add I've tested the array string out in Symfony and it works great. But I can't tell if it can become a vehicle for malicious queries since I intend on using Doctrine's DBAL - I'll take tips on that too (although it seems like a problem regardless for string id's)
However, I am new to all of this and having trouble finding what is "standard". Is this a reasonable solution?
TL;DR: yes, it's fine.
You would probably see an identifier like that described using a level 4 URI Template, with your list of identifiers encoded via a path segment expansion.
Your example template might look something like:
/devices/company{/c_id}/routers{/r_ids}/devices
And you would need to communicate to the template consumer that c_id is a company id, and r_ids is a list of router identifiers, or whatever.
You've seen simplified versions of this on the web: URI templates are generalizations of web forms that read information from input controls and encode the inputs into the query string.
I'm starting to work with the Clockify APIs and I'd like to know if the different IDs are reliable or not? As in, is it a really bad idea to keep ther IDs in my database to know what's what or that's something that would work long term for sure? Thank you
IDs in Clockify represent the identities of their respective entities. They don't change, and are unique across the board, so you can use them in your database if you choose so.
That being said, it's always a good practice when dealing with outside data to assign them your own IDs, that way you're not reliant on contracts that you cannot enforce. Provide every entity with id (your own) and externalId or clockifyId and you won't ever be in position when outside change affected your domain logic.
When representing data models through a RESTful interface it is understood to create top level endpoints that associate to a type/group of objects:
/users
/cars
We can re-use HTTP verbs to enable actions upon these groups (GET to list, POST to create, etc). And when representing a model with a "dependency" (being that it can't exist without a "parent"), we can create deeper endpoints to represent that dependency relationship:
/users/[:id]/tokens
In this case, it makes sense to not have a top-level endpoint of /tokens, as they shouldn't be able to exist without the user.
Many-to-many relationships get a bit more tricky. If two models can have a many-to-many relationship but can also truly exist on their own, it makes sense to give both objects a top-level endpoint and a deeper endpoint for defining that relationship:
/users
/cars
/users/[:id]/cars
/cars/[:id]/users
We can then use PUT and DELETE methods to define those relationships through an HTTP interface: PUT /users/[:user_id]/cars/[:car_id]. It makes sense that running that PUT operation would create a data-model that somehow links the two objects (like a join table in a relational DB).
The tricky part, then, becomes deciding on where to limit the interface to combat redundancy.
Do you allow a GET request to the second-level deep endpoints (GET /users/[:user_id]/cars/[:car_id])? Or do you require that they access the "car" from the top level GET /cars/[:id]?
Now, what if the many-to-many relationship contains meta information? How do you represent that and where do you return it?
For example, what if we wanted to keep track of how many times a user drove a certain car? Where would we return that information? If we return it at the nested endpoint, are we violating REST or being inconsistent if we return the meta information and not the resource? Do we embed the meta information in the requested resource through an attribute of some kind?
Pls advise. :P (but really, thanks)
This is really more of a personal design preference at this point IMHO.
I would personally opt for stopping at /users/[:user_id]/cars/ and then requiring a call to /cars/[:car_id] to get the car information.
If you are including relation specific metadata though, like "how many times a user drove a certain car?" it would make sense to keep that under a deeper relationship like /users/[:user_id]/cars/[:car_id].
Truthfully it's not an exact science. You have to do what is simplest, most expressive, yet still powerful enough for your data model.
You could create a new resource. Something like users/[:user_id]/cars/[:car_id]/stats, whose response includes {drivings_count: 123}. You'd probably only allow a GET of this resource.
I have a site where students can post their own content on it. Whenever they create content they are asked to type in their School Name. Since there will be multiple students from the same school I want to combine all of the duplicate values for the School Name field. I also want to link each School Name so it displays content from only the selected School. Is this possible? If so, how?
you really should consider using the school_name variable for a taxonomy vocabulary (http://drupal.org/node/23405), this way, the field wont be a duplicate, because it references the same term in the vocabulary.
then, when creating the content-type (or user) fields, use the 'relation to taxonomy' field option.
since it seems like you do not know all possible schools, use the tag widget.
in any case, you should think about deduplication, since this builtin method is not considering misspellings or different spellings of the same school name. (although the tag field has a autocomplete function).
a better approach is to present the user with all possible schools, however, that means you need to know all possible schools..
[EDIT] this, of course, is suggesting you are using drupal7 (or later). if not, use cck instead.
From the tags, it seems the situation is Drupal-6.
I think for you to get really good advice, we would need more information about the scope of the project and how many schools would be covered. If you are talking about a worldwide or US-wide scope, there are many schools with the same name, so you might need a school name / City combination (or something) to have a unique identifier for a school. I would suggest you have some way of selecting from a preset list, whether the project covers just a small school district, or there are thousands of schools, that way you won't have issues with duplicate "schools" created during the content creation process.
Since taxonomy terms are not fieldable entities in Drupal 6, you might want to consider creating a separate content type for schools (which would allow you to include location information, etc) and which would be represented as a node reference when students enter content that identifies a school. In Drupal 7, a vocabulary should work for the "school", since you can add fields, but there might be modules that you'd want to use which work better with nodes (a content type) than with fieldable entities (e.g. adding location data or other fields to a vocabulary).
I have come across this issue a few times now, and each time I make a fruitless search to come up with a satisfying answer.
We have a collection resource which returns a representation of the member URIs, as well as a Link header field with the same URIs (and a custom relation type). Often we find that we need specific data from each member in the collection.
At one extreme, we can have the collection return nothing but the member URIs; the client must then query each URI in turn to determine the required data from each member.
At the other extreme, we return all of the details we might want on the collection. Neither of these is perfect; the first can result in a large number of API calls, and the second may return a lot of potentially unneeded information.
Of the two extremes I favour the second in our case, since we rarely use this for more than one sutiation. However, for a more general approach, I wondered if anyone had a nice way of dynamically specifying which details should be included for each member of the collection? I guess a query string parameter would be most appropriate, but I don't want to break the self-descriptiveness of the resource.
I prefer your first option..
At one extreme, we can have the
collection return nothing but the
member URIs; the client must then
query each URI in turn to determine
the required data from each member.
If you are wanting to reduce the number of HTTP calls over the wire, for example calling a service from a handset app (iOS/Android). You can include an additional header to include the child resources:
X-Aggregate-Resources-Depth: 2
Your server side code will have to aggregate the resources to the desired depth.
Sounds like you're trying to reinvent PROPFIND (RFC 4918, Section 9.1).
I regularly contain a subset of elements in each item within a collection resource. How you define the different subsets is really up to you. Whether you do,
/mycollectionwithjustlinks
/mycollectionwithsubsetA
/mycollectionwithsubsetB
or you use query strings
/mycollection?itemfields=foo,bar,baz
either way they are all different resources. I'm not sure why you believe this is affecting the self-descriptive constraint.