As a REST-focused framework, Falcon really should make it easier to generate a URL for a given resource. This will help encourage the use of hypermedia by lowering the barrier to entry.
Prior Art
(Please comment below re a design proposal if you are interested in taking this on, thanks!)
Much of the way that Flask.url_for is implemented in Werkzueg- specifically in the routing code.
The basic premise within that code is to utilize the Flask.url_map._rules_by_endpoint to grab the rule that is mapped to that endpoint and use that to build a URL with the appropriate values for the route. When reading through the compiled router I am not seeing anything obvious that indicates how we could use the same method. Does the compiled router keep a resource class -> url map anywhere?
I can understand that people do want it, but _why_ do people want it? Is generating a URL based on resource a required competency of an API framework? Flask has Jinjna2 bundled for generating HTML so url_for seems to make sense there, within Falcon I haven't seen a need for it yet. Any details on the use case could be helpful.
A reason I could be confused is that, "inter-resource delegation"/"to facilitate one resource calling into another" and url_for seem to be different use cases. When I think of inter-resource delegation, I think of some resource calling an HTTP (get/post/put/delete) method of a different resource without going through the entire web stack. Is that what is meant? If so, how does that relate to url_for?
Places I miss url_for:
For a 201 response, the url of another resource is needed to set the Location header.
POST /gists
Status: 201 Created
Location: https://api.github.com/gists/aa5a315d61ae9438b18d
For a paged collection resource, the url of itself is needed to set the Link header or put them in the resulting response.
GET /user/repos
Status: 200 OK
Link: https://api.github.com/user/repos?page=3&per_page=100; rel="next"
https://api.github.com/user/repos?page=50&per_page=100; rel="last"
Without something like url_for, I have to hard code the route or parse & unparse req.uri.
When I think of inter-resource delegation, I think of some resource calling an HTTP (get/post/put/delete) method of a different resource without going through the entire web stack.
I agree. Perhaps this issue could be renamed if it is about generating the URLs for resources?
Given how much weight Roy Fielding puts on link-driven APIs I'm surprised that a REST-focused framework like Falcon does not have a way to create a URL for a resource.
E.g, for some hypothetical API, if I query /products I would expect a list of product summaries including a link to each product's resource. I suppose Falcon expects users to format their own URLs (note: I'm new here; maybe there's something I'm missing), but it seems there's enough complexity that it's easy to get wrong. What if I have multiple routes to the same resource (e.g., /products and /products/{item})? What about escaping and encoding the URL so it is valid? Something like api.make_url(ProductResource, item_id) that returns /products/1234 or http://example.com/products/1234 would be very useful.
edit: here is essentially how I'm doing it now:
class ProductResource(object):
def on_get(self, req, resp):
urljoin = urllib.parse.urljoin
quote = urllib.parse.quote
data = [{'name': prod.name,
'url': urljoin(req.prefix, 'products/' + quote(prod.id))}
for prod in self.products]
resp.media = data
resp.status = falcon.HTTP_OK
I'm not primarily a web-developer so I'm not sure how good that is. And what if req.prefix has a WSGI app part after the host? Maybe I should use '/products/' for this case?
Since #1228 speaks to delegating a response to another resource/route, I think it makes sense to rename this issue to focus solely on generating the hyperlink.
Most helpful comment
Places I miss
url_for:For a 201 response, the url of another resource is needed to set the
Locationheader.POST /gists
Status: 201 Created
Location: https://api.github.com/gists/aa5a315d61ae9438b18d
For a paged collection resource, the url of itself is needed to set the
Linkheader or put them in the resulting response.GET /user/repos
Status: 200 OK
Link: https://api.github.com/user/repos?page=3&per_page=100; rel="next"
https://api.github.com/user/repos?page=50&per_page=100; rel="last"
Without something like
url_for, I have to hard code the route or parse & unparsereq.uri.