Path parameters are one of the first things you need to use when building REST APIs. In this post, we will learn how to use FastAPI path parameters with validations.
Validations are a key aspect of any robust application. FastAPI uses Pydantic to handle data validations under the hood. Therefore, we get all the benefits of using Pydantic while not worrying too much about validations. This makes it easy for developers to focus on their application logic.
In case you are new to FastAPI, you can start with this detailed post on getting started with FastAPI.
1 – Path Parameters
We can declare path parameters or variables using Python format string.
See below example:
from fastapi import FastAPI
app = FastAPI()
@app.get("/books/{book_id}")
def get_book_by_id(book_id):
return {"book_id": book_id}
Here, the value of the book_id is passed to the function as argument. We can run this example and access the endpoint. We get the below output when we hit http://localhost:8000/books/test.
{"book_id":"test"}
2 – Path Parameters with Types
We can also provide data types for our path parameters.
See below example:
@app.get("/authors/{author_id}")
def get_author_by_id(author_id: int):
return {"author_id": author_id}
Here, we use standard Python annotations to specify that author_id is an integer. If we hit the endpoint http://localhost:8000/authors/test, we get the below error response:
{
"detail": [{
"loc": ["path", "author_id"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}]
}
Basically, the endpoint only expects an integer value for author_id. In other words, FastAPI validates the incoming path parameters and provides an appropriate response.
Even the API documentation reflects the proper data type. See below Swagger screenshot when we access the http://localhost:8000/docs.
Another interesting point to note here is data conversion. When we hit http://localhost:8000/authors/5, we get the below response.
{"author_id":5}
As you can see, the output is a number. In other words, FastAPI converts the string “5” to numeric 5 automatically for us.
3 – Path Order
Often we need to make paths with similar structure. This happens when the fixed path is the same. As an example, consider we have two endpoints as below:
/books/default
/books/{book_id}
The first one returns a default book. The second one returns the book with a particular book_id. In this case, the order of declaring these endpoints is important. The paths are evaluated in order. Therefore, we need to declare /books/default before /books/{book_id}. See below:
@app.get("/books/default")
def get_default_books():
return {"book_name": "The Eye of the World"}
@app.get("/books/{book_id}")
def get_book_by_id(book_id):
return {"book_id": book_id}
This arrangement will ensure that both endpoints are properly invoked.
4 – Path Parameter Predefined Values
Many times, the API path parameters have a set of possible values. In this case, we can use an Enum to define such a set and use it in our function declaration.
See below example:
from enum import Enum
from typing import Optional
from fastapi import FastAPI
app = FastAPI()
class BookCode(str, Enum):
eotw = "eotw"
tgh = "tgh"
tdr = "tdr"
movies_db = [{"movie_name": "The Fellowship of the Ring"}, {"movie_name": "The Two Towers"}, {"movie_name": "The Return of the King"}]
@app.get("/books-by-code/{book_code}")
def get_books_by_name(book_code: BookCode):
if book_code == BookCode.eotw:
return {"book_code": book_code, "book_name": "Eye of the World"}
if book_code.value == "tgh":
return {"book_code": book_code, "book_name": "The Great Hunt"}
if book_code.value == BookCode.tdr.value:
return {"book_code": book_code, "book_name": "The Dragon Reborn"}
We first create an enum class by importing Enum library. This is basically a sub class that inherits from str and Enum. Next, we declare three items in the Enum class. By inheriting from the str class, the API docs will know that we expect a string value as input.
Next, we use the new class as annotation for the path parameter book_code. Within the function, we use different approaches to compare the input to our enum class member and return appropriate book details.
We can also check the API docs where the various possible inputs are also present in dropdown format.
If we pass any value other than the valid values, we get the below error response.
{
"detail": [{
"loc": ["path", "book_code"],
"msg": "value is not a valid enumeration member; permitted: 'eotw', 'tgh', 'tdr'",
"type": "type_error.enum",
"ctx": {
"enum_values": ["eotw", "tgh", "tdr"]
}
}]
}
Conclusion
With this, we have successfully learnt how to use FastAPI Path Parameters with Validations. From a validation perspective, we have considered data types as well as Enum values and how we can configure our endpoint declaration to help with validating the input.
In the next post, we will be looking into FastAPI Query Parameters.
If you have any comments or queries, please feell free to write in the comments section below.
2 Comments
Fer_B · March 9, 2022 at 6:00 pm
Excellent article, thanks!
A small correction: on first example, the URL says:
“http://localhost:8000/test”
but should be:
“http://localhost:8000/books/test”
Best regards!
Saurabh Dashora · March 11, 2022 at 9:49 am
Thanks for pointing it out! Updated the article.