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/fooopen in new window, 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/3open in new window, 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/fooopen in new window, 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.2open in new window

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 Pydanticopen in new window, 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/docsopen in new window, you will see an automatic, interactive, API documentation. Notice that the path parameter is declared to be an integer:

Swagger UI path parameters

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).

Last update: 9/26/2022, 10:04:29 PM