FastAPI Souce: https://github.com/tiangolo/fastapi FastAPI Document: https://fastapi.tiangolo.com
Target: Path Parameters (https://fastapi.tiangolo.com/tutorial/path-params/)
FastAPI tutorial notes.
This is a continuation of FastAPI Tutorial Note 1. FastAPI installation and server startup are the same as above, so please check the above article.
Basically, I refer to the official tutorial of Fast API, but I omit some parts or change the order for my own learning. Please refer to the official documentation for the correct and detailed information.
Since I am a beginner of Web & Python and rely on Google and DeepL for translation, I would appreciate it if you could point out any mistakes.
Ubuntu 20.04 LTS Python 3.8.2 pipenv 2018.11.26
--Understanding the ** Path parameter ** of FastAPI
Intro
FastAPI allows you to declare parameters and variables for paths in the same syntax as Python format.
Save the following code as main.py
.
main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
At this time, the ʻitem_id of the decorator function
@app.get ("/ items / {item_id}") is the ** Path parameter **, which is passed as the argument ʻitem_id
to the function argument.
As an example, after saving this file, start the server (ʻu vicorn main: app --reload) and open http://127.0.0.1:8000/items/foo in your browser. You should get a JSON-formatted text with the string
foo assigned to the variable ʻitem_id
, as shown below.
{"item_id":"foo"}
Actual screen
This is the basis of the ** Path parameter ** in the Fast API.
FastAPI allows you to declare the type of ** Path parameter ** inside a function using standard Python type annotations.
main
~
#Omission
~
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
Notice the line ʻasync def read_item (item_id: int): that declares the function. Here, the argument ʻitem_id
is declared as ʻint(integer type). Type declarations will give you editor support such as error checking and word completion. (In the above code, if I add numbers and strings like
foo = item_id +" bar "`, an error by Type Checker is displayed in my environment (PyCharm).)
If you open http://127.0.0.1:8000/items/3 with a browser with the function part of main.py changed to the above code, you will see the following.
{"item_id":3}
It should be noted here that the value received by the function read_item
and displayed as the return value is not a string but an integer 3
.
By default, all input to the URL is interpreted as a string, but Python's type declaration automatically ** parsing ** (parses) the request and converts it to an integer type.
Http://127.0.0.1 with code that removes the part that declares the type as a trial (returns the part of ʻasync def read_item (item_id: int):
to ʻasync def read_item (item_id):
) If you access: 8000 / items / 3, it will be displayed as the string" 3 "
.{"item_id":"3"}
Also, if you access the Path parameter declared as ʻint` type with a URL like http://127.0.0.1:8000/items/foo, the following Http error will be returned.
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
This is because the given parameter (str
type" foo ") cannot be converted to ʻinttype, so the ** Data validation ** function of FastAPI worked. Similar to the
str type above, if you give a floating point
float` type (such as http://127.0.0.1:8000/items/4.2) you will get a similar error.
In addition, it was confirmed that if a value of ʻint
type is given to a parameter declared with
float` type, it will be converted.
From the above, you can see that the Fast API performs exactly the same validation as the Python type declaration.
Notice that the error message also specifies the point where validation did not pass (in this case the " path "
parameter variable " item_id "
). It will be very useful for developing and debugging code that interacts with the API.
Preventing bugs by type checking is one of the important features of FastAPI.
After checking the above code, check http://127.0.0.1:8000/docs in your browser. The interactive API documentation is automatically generated, as shown below. (Click the blue tab labeled GET
under default
near the center of the screen to expand it.)
There is ʻinteger under Name ʻitem_id
of Parameters
. You can see that the Python type declaration is also reflected in the interactive documentation (integrated into the Swagger UI).
This document gives you a lot of information about the API and can be shared with designers and frontside people.
In addition, this document is based on the schema generated by FastAPI, but the generated schema is OpenAPI compliant, and various compatible tools have been developed. As I confirmed in Memo 1, there is an alternative document using ReDoc in FastAPI. Try accessing http://127.0.0.1:8000/redoc.
It's a rough personal impression, but Swagger UI is an interactive document that can actually generate a request and check the result, while ReDoc is a static and simple document. Perhaps there are more ways to use it and other tools, but I'm not sure at all due to lack of study. I'm sorry. I would like to update it as soon as I learn.
For now, I feel that the Swagger UI, which can generate requests and see the results, is convenient.
Pydantic Returning to the main topic, all type annotations that are useful for document generation like this are done by a library called ** Pydantic **. The following article was helpful for Pydantic. Introduction to Pydantic @ 0622okakyo
** Pydantic ** allows many data types such as str
, float
, bool
to be type-annotated in the same way.
When creating a Path Operation, the path may become fixed.
As an example, suppose you have a path called / users / me
for users to get their own data.
At this time, let's assume that there is also a path called / users / {user_id}
that retrieves data about another user at the same time using ʻuser_id`.
Path Operation in FastAPI is evaluated in order.
Therefore, the path / users / me
must be declared before the path/ users / {user_id}
as shown below.
main
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}
If the path / users / me
is declared after the path/ users / {user_id}
as shown below, the value me
will be passed to the FastAPI as a parameter ʻuser_id`.
main_miss
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}") # <- "/users/me"If you can get the GET method"me"Is passed
async def read_user(user_id: str):
return {"user_id": user_id}
@app.get("/users/me") # <- "/users/me"This function is not called because has already been evaluated
async def read_user_me():
return {"user_id": "the current user"}
If you have a Path Operater that accepts path parameters and you want to predefine valid values for those parameters, we recommend using the Python standard module ʻEnum`.
First, let's create an ʻEnum` class.
Import ʻEnum and create a subclass that inherits
str and ʻEnum
.
By inheriting str
, the API document can know in advance that the value to be set is a string type, and it will be rendered correctly.
Next, create a class attribute with a fixed value that is a valid value.
main
from enum import Enum # <-Import Enum
from fastapi import FastAPI
class ModelName(str, Enum): # <-Class inheritance
alexnet = "alexnet" # <-Class attributes(Fixed value)Creation
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
We will define the path parameters using the defined class.
Use the created enum class (ModelName
) to create a path parameter with type annotations.
main
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/model/{model_name}")
async def get_model(model_name: ModelName): # <-Path parameter with type annotation
if model_name == ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
Check the documentation (http://127.0.0.1:8000/docs). The values available for path parameters are predefined, so the document interactively displays and selects those values.
GET
tab and then click Try it out
at the top of the tab.model_name
becomes selectable.(The screenshot below is taken from the Official Document. I couldn't get a screenshot of the selection screen well in my environment. )
main
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/model/{model_name}")
async def get_model(model_name: ModelName):
if model_name == ModelName.alexnet: # <- ①
return {"model_name": model_name, "message": "Deep Learning FTW!"} # <- ③
if model_name.value == "lenet": # <- ②
return {"model_name": model_name, "message": "LeCNN all the images"} # <- ③
return {"model_name": model_name, "message": "Have some residuals"} # <- ③
For more information, please refer to Python official document enum --- Support for enums.
Here, compare with the created ModelName
enumeration type member (①), get the enumeration type value by .value
and compare it (②), and use the enumeration type member as the return value (③). I have.
Even in the case of a nested JSON body like the dictionary type dict
, it is possible to return the value of Path Operater as an enum member. In this case, it is converted to the corresponding value (str
in the above) and returned to the client.
If you get it in an interactive document or browser, you'll get a JSON return value like this, for example.
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
For example, suppose you have a Path Operation with the path / files / {file_path}
.
But if file_path
itself has a path as a value (like file_path
= home / johndoe / myfile.txt
),
The URL will look like this:
http://127.0.0.1:8000/files/home/johndoe/myfile.txt
OpenAPI does not support the declaration of path parameters with paths as values as described above because it leads to the definition of tests and the occurrence of difficult scenarios.
However, by using Starlette's internal tools, the Fast API allows you to declare such path parameters. (However, it is not possible to confirm that the path parameter itself contains the path in the API document that conforms to OpenAPI. * The document itself works)
Declare the path parameter, including the path, using a URL similar to the following:
/files/{file_path:path}
In the above, the path parameter is file_path
, and the: path
part specifies that the path parameter contains the path.
The usage example is as follows.
main
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
Next time it will be a query parameter.
Recommended Posts