Path Parameters¶
You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
fromfastapiimportFastAPIapp=FastAPI()@app.get("/items/{item_id}")asyncdefread_item(item_id):return{"item_id":item_id}The value of the path parameteritem_id will be passed to your function as the argumentitem_id.
So, if you run this example and go tohttp://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:
fromfastapiimportFastAPIapp=FastAPI()@app.get("/items/{item_id}")asyncdefread_item(item_id:int):return{"item_id":item_id}In this case,item_id is declared to be anint.
Check
This will give you editor support inside of your function, with error checks, completion, etc.
Dataconversion¶
If you run this example and open your browser athttp://127.0.0.1:8000/items/3, you will see a response of:
{"item_id":3}Check
Notice that the value your function received (and returned) is3, as a Pythonint, not a string"3".
So, with that type declaration,FastAPI gives you automatic request"parsing".
Data validation¶
But if you go to the browser athttp://127.0.0.1:8000/items/foo, you will see a nice HTTP error of:
{"detail":[{"type":"int_parsing","loc":["path","item_id"],"msg":"Input should be a valid integer, unable to parse string as an integer","input":"foo"}]}because the path parameteritem_id had a value of"foo", which is not anint.
The same error would appear if you provided afloat instead of anint, as in:http://127.0.0.1:8000/items/4.2
Check
So, with the same Python type declaration,FastAPI gives you data validation.
Notice that the error also clearly states exactly the point where the validation didn't pass.
This is incredibly helpful while developing and debugging code that interacts with your API.
Documentation¶
And when you open your browser athttp://127.0.0.1:8000/docs, you will see an automatic, interactive, API documentation like:

Check
Again, just with that same Python type declaration,FastAPI gives you automatic, interactive documentation (integrating Swagger UI).
Notice that the path parameter is declared to be an integer.
Standards-based benefits, alternative documentation¶
And because the generated schema is from theOpenAPI standard, there are many compatible tools.
Because of this,FastAPI itself provides an alternative API documentation (using ReDoc), which you can access athttp://127.0.0.1:8000/redoc:

The same way, there are many compatible tools. Including code generation tools for many languages.
Pydantic¶
All the data validation is performed under the hood byPydantic, so you get all the benefits from it. And you know you are in good hands.
You can use the same type declarations withstr,float,bool and many other complex data types.
Several of these are explored in the next chapters of the tutorial.
Order matters¶
When creatingpath operations, you can find situations where you have a fixed path.
Like/users/me, let's say that it's to get data about the current user.
And then you can also have a path/users/{user_id} to get data about a specific user by some user ID.
Becausepath 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}:
fromfastapiimportFastAPIapp=FastAPI()@app.get("/users/me")asyncdefread_user_me():return{"user_id":"the current user"}@app.get("/users/{user_id}")asyncdefread_user(user_id:str):return{"user_id":user_id}Otherwise, the path for/users/{user_id} would match also for/users/me, "thinking" that it's receiving a parameteruser_id with a value of"me".
Similarly, you cannot redefine a path operation:
fromfastapiimportFastAPIapp=FastAPI()@app.get("/users")asyncdefread_users():return["Rick","Morty"]@app.get("/users")asyncdefread_users2():return["Bean","Elfo"]The first one will always be used since the path matches first.
Predefined values¶
If you have apath operation that receives apath parameter, but you want the possible validpath parameter values to be predefined, you can use a standard PythonEnum.
Create anEnum class¶
ImportEnum and create a sub-class that inherits fromstr and fromEnum.
By inheriting fromstr the API docs will be able to know that the values must be of typestring and will be able to render correctly.
Then create class attributes with fixed values, which will be the available valid values:
fromenumimportEnumfromfastapiimportFastAPIclassModelName(str,Enum):alexnet="alexnet"resnet="resnet"lenet="lenet"app=FastAPI()@app.get("/models/{model_name}")asyncdefget_model(model_name:ModelName):ifmodel_nameisModelName.alexnet:return{"model_name":model_name,"message":"Deep Learning FTW!"}ifmodel_name.value=="lenet":return{"model_name":model_name,"message":"LeCNN all the images"}return{"model_name":model_name,"message":"Have some residuals"}Tip
If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learningmodels.
Declare apath parameter¶
Then create apath parameter with a type annotation using the enum class you created (ModelName):
fromenumimportEnumfromfastapiimportFastAPIclassModelName(str,Enum):alexnet="alexnet"resnet="resnet"lenet="lenet"app=FastAPI()@app.get("/models/{model_name}")asyncdefget_model(model_name:ModelName):ifmodel_nameisModelName.alexnet:return{"model_name":model_name,"message":"Deep Learning FTW!"}ifmodel_name.value=="lenet":return{"model_name":model_name,"message":"LeCNN all the images"}return{"model_name":model_name,"message":"Have some residuals"}Check the docs¶
Because the available values for thepath parameter are predefined, the interactive docs can show them nicely:

Working with Pythonenumerations¶
The value of thepath parameter will be anenumeration member.
Compareenumeration members¶
You can compare it with theenumeration member in your created enumModelName:
fromenumimportEnumfromfastapiimportFastAPIclassModelName(str,Enum):alexnet="alexnet"resnet="resnet"lenet="lenet"app=FastAPI()@app.get("/models/{model_name}")asyncdefget_model(model_name:ModelName):ifmodel_nameisModelName.alexnet:return{"model_name":model_name,"message":"Deep Learning FTW!"}ifmodel_name.value=="lenet":return{"model_name":model_name,"message":"LeCNN all the images"}return{"model_name":model_name,"message":"Have some residuals"}Get theenumeration value¶
You can get the actual value (astr in this case) usingmodel_name.value, or in general,your_enum_member.value:
fromenumimportEnumfromfastapiimportFastAPIclassModelName(str,Enum):alexnet="alexnet"resnet="resnet"lenet="lenet"app=FastAPI()@app.get("/models/{model_name}")asyncdefget_model(model_name:ModelName):ifmodel_nameisModelName.alexnet:return{"model_name":model_name,"message":"Deep Learning FTW!"}ifmodel_name.value=="lenet":return{"model_name":model_name,"message":"LeCNN all the images"}return{"model_name":model_name,"message":"Have some residuals"}Tip
You could also access the value"lenet" withModelName.lenet.value.
Returnenumeration members¶
You can returnenum members from yourpath operation, even nested in a JSON body (e.g. adict).
They will be converted to their corresponding values (strings in this case) before returning them to the client:
fromenumimportEnumfromfastapiimportFastAPIclassModelName(str,Enum):alexnet="alexnet"resnet="resnet"lenet="lenet"app=FastAPI()@app.get("/models/{model_name}")asyncdefget_model(model_name:ModelName):ifmodel_nameisModelName.alexnet:return{"model_name":model_name,"message":"Deep Learning FTW!"}ifmodel_name.value=="lenet":return{"model_name":model_name,"message":"LeCNN all the images"}return{"model_name":model_name,"message":"Have some residuals"}In your client you will get a JSON response like:
{"model_name":"alexnet","message":"Deep Learning FTW!"}Path parameters containing paths¶
Let's say you have apath operation with a path/files/{file_path}.
But you needfile_path itself to contain apath, likehome/johndoe/myfile.txt.
So, the URL for that file would be something like:/files/home/johndoe/myfile.txt.
OpenAPI support¶
OpenAPI doesn't support a way to declare apath parameter to contain apath inside, as that could lead to scenarios that are difficult to test and define.
Nevertheless, you can still do it inFastAPI, using one of the internal tools from Starlette.
And the docs would still work, although not adding any documentation telling that the parameter should contain a path.
Path convertor¶
Using an option directly from Starlette you can declare apath parameter containing apath using a URL like:
/files/{file_path:path}In this case, the name of the parameter isfile_path, and the last part,:path, tells it that the parameter should match anypath.
So, you can use it with:
fromfastapiimportFastAPIapp=FastAPI()@app.get("/files/{file_path:path}")asyncdefread_file(file_path:str):return{"file_path":file_path}Tip
You might need the parameter to contain/home/johndoe/myfile.txt, with a leading slash (/).
In that case, the URL would be:/files//home/johndoe/myfile.txt, with a double slash (//) betweenfiles andhome.
Recap¶
WithFastAPI, 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 them once.
That's probably the main visible advantage ofFastAPI compared to alternative frameworks (apart from the raw performance).







