Fastapi: [QUESTION] Support both /users/{user_id} and /users/_bulk

Created on 22 May 2019  Â·  14Comments  Â·  Source: tiangolo/fastapi

I have /users API that supports PUT and GET for /users/{user_id}. Now I want to introduce a bulk API to update several users at once. I thought about introducing /users/_bulk endpoint for PUT operations - we don't allow _ in our IDs hence there is no URL collision.

However when trying to implement it in FastAPI they do collide and /users/_bulk gets routed to /users/{user_id}.

In our old system we use aiohttp which stores routes just a a list. Hence, to solve the above "collision" we just sort routes as

paths = sorted(paths, key=lambda x: x.count("{"))

which effectively pushes more vague, pattern routes down the list.

Do you think it will be a valid request for FastAPI to do the same?

enhancement

Most helpful comment

/CC @motinani, @worroc

Hi @tiangolo,
I moved to new place where I'm not involved with FastAPI anymore. I'd like to once again express my deep gratitude to you for creating this amazing project!

All 14 comments

Maybe not what you want, but have you tried changing the routes order?

Yeap, I tried to put @router.put("/users/_bulk", ...) before @router.put("/users/{user_id}"), but it didn't help.
AFAIR, modules are still unordered dicts, even in Python3.6+.

Just mentioned that to you because if I 'm not mistaken routes are
evaluated orderly:
https://www.starlette.io/routing/

Le mer. 22 mai 2019 à 8:02 AM, Zaar Hai notifications@github.com a écrit :

Yeap, I tried to put @router.put("/users/_bulk", ...) before
@router.put("/users/{user_id}"), but it didn't help.
AFAIR, modules are still unordered dicts, even in Python3.6+.

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/tiangolo/fastapi/issues/244?email_source=notifications&email_token=AAINSPRJ7HLTHF36F7I2EZLPWTOWRA5CNFSM4HOQ26Y2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODV57TSY#issuecomment-494664139,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAINSPQPH5JV2GBE64WVUETPWTOWRANCNFSM4HOQ26YQ
.

Yeah, I saw that as well. And now I'm thinking that since python reads code top-down, it should've worked...

Another thing worth trying is Starlette urlconvertors

Le mer. 22 mai 2019 à 8:10 AM, Zaar Hai notifications@github.com a écrit :

Yeah, I saw that as well. And now I'm thinking that since python reads
code top-down, it should've worked...

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/tiangolo/fastapi/issues/244?email_source=notifications&email_token=AAINSPXFGOUDKC6FPT2ZU6DPWTPU5A5CNFSM4HOQ26Y2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODV6ABPQ#issuecomment-494665918,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAINSPTF3V2FJPQ2VZ3XDWTPWTPU5ANCNFSM4HOQ26YQ
.

just tried and it seems the underscore makes the route not registered in the APIRouter

It gets interesting :)
I had other issue that confused me to believe there is a problem. I re-read the docs you send and they confirm that my case should be supported with explicit ordering.

So now things work, even regardless of the definition order! I.e. exactly the same way I wanted originally.

I even tried PUT /users/{user_id} with user_id=_bulk and it still landed on the correct handler.

/users/_bulk accepts List[User] model, while users/{user_id] accepts just User model that's the only difference between request to /users/{user_id} with user_id=_bulk and request to /users/_bulk. I wonder how does FastAPI distinguish between them. Does it try to cast the body? @tiangolo, can you please comment?

I just threw a testcase that shows a bug in APIRouter
https://github.com/euri10/fastapi/tree/underscore_route
while a leading underscore route appears correctly in app.routes, the list remains empty in router.routes

you got an issue because the route simply doesnt exist, you got a 404 ?

edit: bah no, works fine :mag:

So, paths should be evaluated in order.

This section warns about it: https://fastapi.tiangolo.com/tutorial/path-params/#order-matters (that section wasn't always there).

It seems strange if it seemed to work at some point... :thinking:

I just released a version that uses the latest (just released) version of Starlette. I was thinking that it could have some new logic around that. But checking the code, I don't see any change in it...

About the feature request of sorting the routes by specificity, I think it should be in Starlette directly, as FastAPI doesn't override that. FastAPI tries to re-use as much as possible from Starlette, without overriding it, to make the most of all the features already provided by Starlette.

Found the reason why it seemed to work regardless of the order - by accident I defined bulk endpoint as /users/_bulk/ with trailing / while /users/{user_id} without.

Such a waste of everyone's time :|

Thank you @euri10 and @tiangolo for looking into this.

Hehe cool. You can declare everything with slashes and it will redirect versions without slashes to versions with slashes.

There are still some cases where it's useful to have non-slashes only routes.

Not exactly - upon 302, client will switch from e.g. POST to get. So curl -L -X POST /users/_bulk will actually fail. May be once https://github.com/encode/starlette/issues/464 lands, it will work transparently as long as redirects are enabled in clients.

Though I think I prefer not to rely on redirects, since a). Not all clients, particularly backend clients (svc to svc) enable them by default; b). backend clients will not cache the response (because usually they lack cache all together) so it will result in extra roundtrip to server. I prefer to fail early and explicitly.
What's your experience on the subject?

```

  • Trying 127.0.0.1...
  • Connected to localhost (127.0.0.1) port 8000 (#0)

POST /api/v1/users/_bulk HTTP/1.1
Host: localhost:5095
User-Agent: curl/7.47.0
accept: application/json
Content-Type: application/json
Content-Length: 2

  • upload completely sent off: 2 out of 2 bytes
    < HTTP/1.1 302 Found
    < date: Thu, 23 May 2019 11:00:50 GMT
    < server: uvicorn
    < location: http://localhost:8000/api/v1/users/_bulk/
    < transfer-encoding: chunked
    <
  • Ignoring the response-body
  • Connection #0 to host localhost left intact
  • Issue another request to this URL: 'http://localhost:8000/api/v1/pets/_bulk/'
  • Switch from POST to GET # <----------------------------------------!!!!!
    ...
    ````

Hey @haizaar, thanks for your patience.

This should be solved in the latest versions after https://github.com/encode/starlette/pull/563.

/CC @motinani, @worroc

Hi @tiangolo,
I moved to new place where I'm not involved with FastAPI anymore. I'd like to once again express my deep gratitude to you for creating this amazing project!

Was this page helpful?
0 / 5 - 0 ratings