Path parameters
Up until now we made an example without the possibility of us sending along any parameters with our API call.
Path parameters
You can include this functionality by declaring path "parameters" or "variables" with the same syntax used by Python format strings:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
Notice that the value of the path parameter item_id
will be passed to your function as the argument item_id
.
So, if you run this example and go to http://127.0.0.1:8000/items/foo, you will see a response of:
{"item_id":"foo"}
Path parameters with types
You can declare the type of a path parameter in the function, using standard Python type annotations:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
In this case, item_id
is declared to be an int
.
Info
This will also give you editor support inside of your function, with error checks, completion, etc.
Data conversion or parsing
If you run this example and open your browser at http://127.0.0.1:8000/items/3, you will see a response of:
{"item_id":3}
Notice that the value your function received (and returned) is 3
, as a Python int
, not a string "3"
.
So, with that type declaration, FastAPI gives you a way to automatically convert the string that comes from an HTTP request into Python type data. This is also called parsing.
Data validation
But if you go to the browser at http://127.0.0.1:8000/items/foo, you will see a nice HTTP error of:
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
This is because the path parameter item_id
had a value of "foo"
, which cannot be parsed to an int
.
The same error would appear if you provided a float
instead of an int
, as in: http://127.0.0.1:8000/items/4.2
You can use the same type declarations with str
, float
, bool
and many other complex data types.
Pydantic
So, with the same Python type declaration, FastAPI gives you data validation. All the data validation is performed under the hood by Pydantic, so you get all the benefits from it. And you know you are in good hands.
Notice that the error also clearly states exactly the point where the validation didn't pass.
Then also take a look at the documentation. When you open your browser at http://127.0.0.1:8000/docs, you will see an automatic, interactive, API documentation. Notice that the path parameter is declared to be an integer:
Again, just with that same Python type declaration, FastAPI gives you automatic, interactive documentation (integrating Swagger UI).
The order of path operations
When creating path operations, you can find situations where you have a fixed path:
/users/me
: to get data about the current user./users/{user_id}
to get data about a specific user by some user ID
Because path operations are evaluated in order, you need to make sure that the path for /users/me
is declared before the one for /users/{user_id}
:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/me")
async def read_user_me():
return {"user_id": "the current user"}
@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id}
Otherwise, the path for /users/{user_id}
would first match for /users/me
. The framework would then assume that it's receiving a parameter user_id
with a value of "me"
.
Similarly, you cannot redefine a path operation:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users")
async def read_users():
return ["Rick", "Morty"]
@app.get("/users")
async def read_users2():
return ["Bean", "Elfo"]
The first one will always be used since the path matches first.
Path parameters containing paths
Let's say you have a path operation with a path /files/{file_path}
.
But you need file_path
itself to contain a path, like home/johndoe/myfile.txt
.
So, the URL for that file would be something like: /files/home/johndoe/myfile.txt
.
OpenAPI doesn't support a way to declare a path parameter to contain a path inside, as that could lead to scenarios that are difficult to test and define.
Nevertheless, you can still this in FastAPI, using one of the internal tools from Starlette. The automatic documentation would still work, although it will not be adding any mention telling that the parameter should contain a path.
Path convertor
Using an option directly from Starlette you can declare a path parameter containing a path using a URL like:
@app.get("/files/{file_path:path}")
In this case, the name of the parameter is file_path
, and the last part, :path
, tells it that the parameter should match any path.
So, you can use it with:
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
Tips
It can happen that you want parameter to contain a leading slash (/
), like so /home/johndoe/myfile.txt
.
In that case, the URL would be: /files//home/johndoe/myfile.txt
, with a double slash (//
) between files
and home
.
Recap
With FastAPI, by using short, intuitive and standard Python type declarations, you get:
- Editor support: error checks, autocompletion, etc.
- Data parsing
- Data validation
- API annotation and automatic documentation
And you only have to declare all of the features once.
That's probably the main visible advantage of FastAPI compared to alternative frameworks (apart from the raw performance).