@jjrushford The practical value driving this, is that the Go standard library's HTTP router can't do path matching. The same is likely true for any simple language (e.g. C vs Java). Query parameters let us use the built-in router, saving either a complex bit of routing code, or a large third party library. They should also be faster, if performance matters. Most routers use the easier regex matching, which can be slow. Go's standard router iterates over all routes and does a string comparison on every one, for every request. Fast methods exist, like a trie, but they're complex to implement and maintain. While query params are put in a hashmap passed to the request handler.
In terms of ideology, you're right, the widely-accepted practice is for path parameters to specify a "resource," and for query params to "filter." That said, there's nothing in any RFC condoning or requiring that, as far as I'm aware. (RFC3986 says "The query component contains non-hierarchical data that, along with data in the path component (Section 3.3), serves to identify a resource".) > what do you return if the query parameter is left off A 4xx, same as if they omitted the path field. >I know there have been a lot of complaints about having to fetch resources by id instead of name. This will take care of that. @mitchell852 +1. Getting rid of IDs in the database and using real data as keys will be even better, if we can ever get to that point. But that's a different discussion. Also to @mitchell852 's point on ambiguity, paths make compound keys unintuitive, too. For example, is it /api/profile-parameters/foo/bar, or /api/profile-parameters/bar/foo, or /api/profile-parameters/profile/foo/parameter/bar, or /api/profile-parameters/parameter/bar/profile/foo, or /api/profile/foo/parameter/bar, or /api/parameter/bar/profile/foo, or /api/profile/foo/bar? You could guess, but there's no unambiguously obvious right answer, you'd have to look up the docs. Whereas with query params, /api/profile-parameters?profile=foo¶meter=bar is obvious. Maybe another way to think about it, for the ideological "resource", is that the database table (or view) is the resource, and the query params are filtering that table how you want. Personally, I'm inclined to put the practical above the ideological in this case. But I don't care enough to fight for it. A regex router in Go is 50 lines of code; it's 50 complex lines, but it's 50 lines. If other people have strong opinions, I'll step back. On Thu, Apr 5, 2018 at 8:50 AM, Jeremy Mitchell <[email protected]> wrote: > John, > > TBH, it's hard to find any hard and fast rules regarding REST. At least I > haven't found any. > > So the question is why B instead of A: > > A. GET api/1.3/deliveryservices/{xmlID}/urisigning > B. GET api/1.3/deliveryservices_urisigningkeys[?optional queryParams] > > I can think of a few reasons: > > 1. query params are unambiguous. for example, they look like ?xmlId=foobar. > there is a key and a value. looking at > api/1.3/deliveryservices/foobar/urisigning, > it may not be clear that foobar is the xmlId of the delivery service. > 2. pattern matching can get screwed up. think of this example. we have 2 > routes defined: > > GET /foos/:id > GET /foos/:name > > which route does this match? GET /foos/42 > > maybe names can be numbers. so is 42 an ID or a Name? not sure. > > 3. we can get away with less route definitions. instead of 3 routes: > > GET /foos <-- get all foos > GET /foos/:id <-- get foo by id > GET /foos/:name <-- get foo by name > > we can simply have 1: > > GET /foos with optional query params (?id= or ?name=) > > I know there have been a lot of complaints about having to fetch resources > by id instead of name. This will take care of that. > > 4. Nested routes can start to get a little nasty and uneccessary imo. GET > /foos/:id/bars/:id/comments/:id (there can be a lot of permutations of > that) where you really only need the id of the comment to retrieve it: GET > /foo_bar_comments?id=4 > > 5. There are some built in benefits in Go but I'll have to defer to Rob > and/or Dylan regarding that. > > Off the top of my head, that's all i can think of. If we plan to use tools > such as Swagger for our API, we need to agree on a standard for the API and > stick to it. We're simply trying to find that standard. :) > > Jeremy > > > > > > On Wed, Apr 4, 2018 at 10:29 PM, John Rushford <[email protected]> > wrote: > > > Why the change? It’s my understanding that path parameters should be > used > > to specify a particular resource > > and query parameters should be used to sort/filter the query. Why use a > > query parameter to specify a particular > > resource? Is this REST API best practice? > > > > What about sub resource queries such as using the following: > > > > GET api/1.3/deliveryservices/{xmlID}/urisigning > > > > where you are requesting a particular urisigning keys sub resource for > the > > particular deliveryservice resource. You can make it work > > with an xmlid query parameter but what do you return if the query > > parameter is left off, all uri signing keys? Is that useful? > > > > John > > > > > On Apr 4, 2018, at 3:23 PM, Jeremy Mitchell <[email protected]> > > wrote: > > > > > > tbh i'm not sure about versioning. I was just trying to suggest that > new > > > routes be formulated this way per the new API guidelines: > > > > > > GET /foos[?id, name, etc=] > > > POST /foos > > > PUT /foos [?id, name, etc=] > > > DELETE /foos [?id, name, etc=] > > > > > > instead of the old way: > > > > > > GET /foos > > > GET /foos/:id > > > POST /foos > > > PUT /foos/:id > > > DELETE /foos/:id > > > > > > The difference being the use of query params over route/path params. > > > > > > Technically, adding new routes does not break old stuff right so i > don't > > > think that warrants a major version roll. > > > > > > While we're on the subject, what does everyone think if we took this > one > > > step further and made routes handle a request payload with one or more > > > items. For example: > > > > > > GET /foos[?id, name, etc=] > > > POST /foos <-- takes in an array of foos to create > > > PUT /foos <-- takes in an array of foos to update > > > DELETE /foos <-- takes in an array of foos to delete > > > > > > in this scenario, query params only pertain to the GET. The POST, PUT > and > > > DELETE rely on the contents of the request json... > > > > > > Jeremy > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > On Wed, Apr 4, 2018 at 1:55 PM, Robert Butts <[email protected] > > > > > wrote: > > > > > >> That document doesn't mention versions, 1.2 vs 1.3 vs 2.0. > > >> > > >> Just to clarify, changing to query parameters breaks compatibility > with > > 1.2 > > >> and older, so new APIs in that format have to be a new major version, > > i.e. > > >> 2.0, per Semantic Versioning, right? > > >> > > >> On Tue, Apr 3, 2018 at 3:26 PM, Jeremy Mitchell < > [email protected] > > > > > >> wrote: > > >> > > >>> FYI - I've updated the TO API guidelines to reflect our desire to > move > > >> away > > >>> from route/path params and embrace query params in the Golang API. > > >>> > > >>> https://cwiki.apache.org/confluence/display/TC/API+Guidelines > > >>> > > >>> Jeremy > > >>> > > >> > > > > >
