โป FastAPI Tutorial์ ๊ณต๋ถํ๋ฉฐ ์ดํดํ๋๋ก ์์ฑํ ๋ด์ฉ์ผ๋ก, ์ค์ ๋ด์ฉ๊ณผ ๋ค๋ฅด๊ฑฐ๋ ํ๋ฆฐ ๋ถ๋ถ์ด ์์ ์ ์์ต๋๋ค. ๋๊ธ๋ก ์๋ ค์ฃผ์๋ฉด ์ฆ์ ๋ฐ์ํ๊ฒ ์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค.
โป ์ค๊ฐ์ ์ดํดํ์ง ๋ชปํด ์์ด๋ก ์ฎ๊ฒจ๋ ๋ถ๋ถ์ด ์์ต๋๋ค. ์ถํ ์์ ํ๊ฒ ์ต๋๋ค.
https://fastapi.tiangolo.com/ko/tutorial/
1. ์ฒซ๊ฑธ์
1. FastAPI import
from fastapi import FastAPI
2. ์ธ์คํด์ค ์์ฑ
app = FastAPI()
3. ๊ฒฝ๋ก ๋์ ์์ฑ
@app.get('/')
- ๊ฒฝ๋ก: URL์ ๋ง์ง๋ง ๋ถ๋ถ ⇒ https://example.com/item/test ์ ๊ฒฝ๋ก = /item/test
- ๋์: HTTP์ Method ์ค ํ๋
- POST / GET / PUT / DELETE
- ๋ฐ์ดํฐ ์์ฑ / ๋ฐ์ดํฐ ์ฝ๊ธฐ / ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ / ๋ฐ์ดํฐ ์ญ์
- OPTIONS / HEAD / PATCH / TRACE
- POST / GET / PUT / DELETE
4. ๊ฒฝ๋ก ๋์ ํจ์ ์ ์
async def root():
return {"message": "Hello World"}
- URL / ์ ๋ํ ์์ฒญ์ ๋ฐ์ ๋๋ง๋ค ํธ์ถ๋๋๋ก ์์ฑ๋จ.
- ํ์ด์ฌ ํจ์์ ๋์ผ, list, dict, str, int ๋ฑ์ ๋ฐํํ ์ ์๋ค.
2. ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์
- ๋งค๊ฐ๋ณ์ / ๋ณ์๋ฅผ ๊ฒฝ๋ก์ ์ ์ธ ๊ฐ๋ฅ
- ์ด๋ ๊ฒ ํ๊ณ http://127.0.0.1:8000/items/darling ์ ๊ฐ์ด
items
๋ค์ ์๋ฌด ๋ด์ฉ์ ์จ๋ฃ์ผ๋ฉด {"item_id":"darling"}
์ ๊ฐ์ด ๋ณ์๋ก ์ ๋ฌ๋๋ค.
- ์ด๋ ๊ฒ ํ๊ณ http://127.0.0.1:8000/items/darling ์ ๊ฐ์ด
- ํ์ ์ด ์๋ ๋งค๊ฐ๋ณ์
@app.get('/items/{item_id}')
async def read_item(item_id: int):
return {"item_id": item_id}
- ๋งค๊ฐ๋ณ์ ํ์
์ ์ง์ ํ ์ ์๋ค. ์ด๋ ๊ฒ ๋๋ฉด
int
ํ์ด ์๋ ๋ค๋ฅธ ์๋ฃํ๋ค์ ๋ฃ์ ๊ฒฝ์ฐ ์๋ฌ ๋ฐ์.
{
"detail":[
{
"loc":["path","item_id"],
"msg":"value is not a valid integer",
"type":"type_error.integer"
}
]
}
INFO: 127.0.0.1:63901 - "GET /items/darling HTTP/1.1" 422 Unprocessable Entity
๊ณ ์ ๊ฒฝ๋ก๋ฅผ ๊ฐ์ง ๋ฐ์ดํฐ์ ๋ํ ์ ์ธ ์์
- python์ script ๊ธฐ๋ฐ → ์์์๋ถํฐ ์ ์ํ๊ธฐ ๋๋ฌธ์ ๊ณ ์ ๊ฒฝ๋ก๋ฅผ ๊ฐ๋ณ ๊ฒฝ๋ก๋ณด๋ค ๋จผ์ ์ ์ธํด์ค์ผํ๋ค.
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}
- ⇒
/users/me
๋ก ์ ์ํ๋ฉด{"user_id":"the current user"}
๊ฐ ์ ์์ ์ผ๋ก ์ถ๋ ฅ๋๊ณ ,/user/others
๋ก ์ ์ํ๋ฉด{"user_id":"others"}
๋ก ์ ์ ์์ด๋๋ฅผ ์ถ๋ ฅํ๋ค.
@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id} @app.get("/users/me")
@app.get("/users/me")
async def read_user_me():
return {"user_id": "the current user"}
- ⇒
/user/me
๋ก ์ ์ํ์ ๋ ์ ํจ์๊ฐ ๋จผ์ ์ ์๋์ด์ ์๋ ํจ์์me
๊ฐ{user_id}
์๋๋ก ๋ค์ด๊ฐ๊ฒ ๋์ด{"user_id":"me"}
๋ผ๋ ๊ฒฐ๊ณผ๊ฐ ๋์ถ๋๋ค.
์ฌ์ ์ ์ ๊ฒฝ๋ก
- ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์์ฒ๋ผ ์ ๋ถ ์คํ๋์ด์๋ ๊ฒ์ด ์๋๋ผ ์ผ์ด์ค ๋ช ๊ฐ ์ค์ ์ ํํ ์ ์๋๋ก ํ๊ธฐ ์ํ ๊ฒ.
Enum
์์ ํด๋์ค๋ฅผ ํ์ฉํด์ ์ฌ์ ์ ํด๋์ค๋ก ์ ์ํ ์ ์๋ค.
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is 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"}
- ๊ฒฐ๊ณผ
# https://127.0.0.1:8000/models/alexnet
{"model_name":"alexnet","message":"Deep Learning FTW!"}
# https://127.0.0.1:8000/models/lenet
{"model_name":"lenet","message":"LeCNN all the images"}
๊ฒฝ๋ก ํฌํจํ๋ ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์
⇒ /files/{file_path}
์ ๊ฒฝ๋ก ๋์์์ ํ์๋ก ํ๋ file path๊ฐ /root/dnai/myfile.xtx
์ฒ๋ผ ๊ฒฝ๋ก ์์ฒด์ธ ๊ฒฝ์ฐ url์ด /files/root/dnai/myfile.txt
๊ฐ ๋๋ค → Open API์์ ์ง์ํ์ง ์์
๊ทธ๋ฌ๋ FastAPI์์๋ ๊ฐ๋ฅ~
/files/{file_path:path}
- ๋งค๊ฐ๋ณ์ ์ด๋ฆ =
file_path
,:path
๋ ๋งค๊ฐ๋ณ์๊ฐ ๊ฒฝ๋ก์ ์ผ์นํด์ผํจ์ ๋ช ์
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
http://127.0.0.1:8000/files/C:/Users/dk866/Desktop/bearing_test/README.md
{"file_path":"C:/Users/dk866/Desktop/bearing_test/README.md"}
3. ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์
๊ฒฝ๋ก ๋งค๊ฐ๋ณ์๊ฐ ์๋ ํจ์ ์์ฒด์ ๋งค๊ฐ๋ณ์ = ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์
from fastapi import FastAPI
app = FastAPI()
item_db = [{'itemname' : "va"}, {'itemname' : "mr"}, {'itemname' : "lk"}, {'itemname' : "ie"}, {'itemname' : "sy"}]
@app.get('/')
async def root():
return 'Hello World'
@app.get('/items/')
async def read_item(skip: int = 0, limit: int = 10): # skip, limit๊ฐ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์
return item_db[skip:skip + limit]
- url ์์์
?
๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์ ์์ญ์ผ๋ก ๋ค์ด๊ฐ๊ณ ,&
๋ก ๋งค๊ฐ๋ณ์๋ฅผ ๊ตฌ๋ถํจ- ex)
http://127.0.0.1:3000/items/?skip=0&limit=3
- ๊ธฐ๋ณธ๊ฐ์ ๋ํ ์ค์ ์ ์ผ๋ฐ ํ์ด์ฌ ํจ์์ ๋์ผ
- ex)
- ์ ํ์ ๋งค๊ฐ๋ณ์
- ๊ธฐ๋ณธ๊ฐ์ None์ผ๋ก ์ค์ ํ ๊ฒฝ์ฐ → ํ์ํ ๊ฒฝ์ฐ ์ฐ๊ณ ์๋๋ฉด ์ ์ฐ๋ ๊ฒ. if๋ฌธ์ผ๋ก ์๋ ๊ฒฝ์ฐ์ ์๋ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํด์ฃผ๋ฉด ๋จ.
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: Union[str, None] = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
- Bool ํ์ ๋ํ ๋ฒ์ฉ์ฑ
- True = 1 = true = on = yes = ๋๋ฌธ์
ํ์ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์
๊ฒฝ๋ก ํจ์ ์์์ ์๋ฃํ ์ ์ธ + ๊ธฐ๋ณธ๊ฐ None์ผ๋ก ์ค์ ํด์ฃผ๋ฉด ํ์ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์
@app.get('/items/')
async def read_item(skip: int, limit: int = 10):
return item_db[skip: skip + limit]
http://127.0.0.1:3000/items/
⇒value_error.missing
http://127.0.0.1:3000/items/?skip=3
⇒[{"itemname":"ie"},{"itemname":"sy"}]
@app.get("/items/{item_id}")
async def read_user_item(
item_id: str, needy: str, skip: int = 0, limit: Union[int, None] = None
):
item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
return item
์ด ๊ฒฝ์ฐ์๋ needy
๋ ํ์, skip
์ ๊ธฐ๋ณธ๊ฐ์ด 0์ธ integer, limit
๋ ์ ํ์ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์๊ฐ ๋๋ค.
4. Request Body
Request Body : ํด๋ผ์ด์ธํธ → API๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ๊ฒ
Response body : API → ํด๋ผ์ด์ธํธ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ๊ฒ
- API์์๋ ๋ Response body๋ฅผ ๋ณด๋ด์ผํ์ง๋ง ํด๋ผ์ด์ธํธ ์ชฝ์์๋ ๊ทธ๋ด ํ์ ์๋ค.
- ํด๋ผ์ด์ธํธ์์ ๋ณด๋ด์ฃผ๋ ๋ฐ์ดํฐ๋ฅผ ‘์ด๋ ๊ฒ ํด์ฃผ์ธ์’ ํ๊ณ ๊ท๊ฒฉ์ ์ฃผ๋ ์๋ฏธ? ์ฝ๊ฒ ํด๋์ค์์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์์ ๊ฐ๋ค์ธ ์ ์๋ค.
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Union
class Item(BaseModel):
name: str
description: Union[str, None] = None # optional
price: float
tax: Union[float, None] = None # optional
app = FastAPI()
@app.post('/items/')
async def create_item(item: Item):
return item
→ docs์์ ์คํ
# request body
{
"name": "puppet",
"description": "pebric doll",
"price": 22480,
"tax": 1237
}
# response body
{
"name": "puppet",
"description": "pebric doll",
"price": 22480,
"tax": 1237
}
# curl
curl -X 'POST' \
'http://127.0.0.1:2000/items/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "puppet",
"description": "pebric doll",
"price": 22480,
"tax": 1237
}'
- ์ ๋ฐ์ดํธ
@app.post('/items/')
async def create_item(item: Item):
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
# request
{
"name": "o",
"description": "s",
"price": 80,
"tax": 1
}
# response
{
"name": "o",
"description": "s",
"price": 80,
"tax": 1,
"price_with_tax": 81
}
- request body + ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์
@app.put("items/{item_id}")
async def create_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()
- request body + ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์ + ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: Union[str, None] = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
5. ํด๋ฆฌ ๋งค๊ฐ๋ณ์์ ๋ฌธ์์ด ๊ฒ์ฆ
⇒ parameter์ ๋ํ ์ถ๊ฐ ์ ๋ณด ์ ๊ณตํ ์ ์๋ค.
@app.get("/itmes/")
async def read_items(q: Union[str, None] = None):
result = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
result.update({"q" : q})
return result
Union[str, None]
์ด๋ผ๊ณ ์ฐ๋ฉด q๋ str์๋๋ฉด None์ด๋ผ๋ ๋ป + None์ผ๋ก ๊ธฐ๋ณธ๊ฐ ์กด์ฌ = ํ์ ์๋๋ผ๋ ๊ฒ์ FastAPI์์ ์ ์ ์์
http://127.0.0.1:8000/itmes/?q=hello
{"items":[{"item_id":"Foo"},{"item_id":"Bar"}],"q":"hello"}
- ์ถ๊ฐ validation
from typing import Union
from typing_extensions import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/itmes/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
result = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
result.update({"q" : q})
return result
Annotated
๋ ๋งค๊ฐ๋ณ์์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๋ค.Query
์์max_length
๋ฅผ ์ง์ ํจ์ผ๋ก์จ Query์ ๋งค๊ฐ๋ณ์๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ค์ด์ค๋ ๋ฐ์ดํฐ๋ฅผ ๊ฑธ๋ฌ์ฃผ๊ธฐ๋ฅผ ์์ฒญํ ์ ์๋ค.- Query๋ฅผ default value๋ก ์ฌ์ฉํ ์ ์๋ค.
Annotated
๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ด ๊ถ์ฅ๋๋ค,- Using
Annotated
is recommended instead of the default value in function parameters, it is better for multiple reasons. ๐คYou could call that same function in other places without FastAPI, and it would work as expected. If there's a required parameter (without a default value), your editor will let you know with an error, Python will also complain if you run it without passing the required parameter.BecauseAnnotated
can have more than one metadata annotation, you could now even use the same function with other tools, like Typer. ๐ํผ - When you don't use
Annotated
and instead use the (old) default value style, if you call that function without FastAPI in other place, you have to remember to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g.QueryInfo
or something similar instead ofstr
). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out. - The default value of the function parameter is the actual default value, that's more intuitive with Python in general. ๐
- Using
ํ์ ํ๋ผ๋ฏธํฐ๋ก ์ง์
q: str
q: Annotated[Union[str, None], Query(min_length=3)]
q: Annotated[str, Query(min_length=3)] = ...
q: Annotated[Union[str, None], Query(min_length=3)] = ... # None ํ์ฉ, ํ์
q: Annotated[str, Query(min_length=3)] = Required # pydantic
๋ฆฌ์คํธ๋ก ๋ฐ๊ธฐ
async def read_items(q: Annotated[Union[List[str], None], Query()] = None):
query_items = {"q": q}
return query_items
http://127.0.0.1:8000/items/?q=hello&q=darling
{"q":["hello","darling"]}
- ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋ฆฌ์คํธ ์ฃผ๊ธฐ๋ ๊ฐ๋ฅ
async def read_items(q: Annotated[Union[List[str], None], Query()] = ["hello", "darling"):
query_items = {"q": q}
return query_items
http://127.0.0.1:8000/items/
{"q":["hello","darling"]}
List
๋์list
๋ ์ฌ์ฉ ๊ฐ๋ฅ
http://127.0.0.1:8000/items/
{"q":["hello","darling"]}
- list ๋ด๋ถ ์๋ฃํ์ด ๋ญ์ง๋ ๊ฒ์ฌํ์ง ์์
์ถ๊ฐ metadata
- title
- description
- Alias(๋ณ๋ช )
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None)
- ์ด๋ฌ๋ฉด url์์ q๊ฐ ์๋๋ผ item-query๋ก ์ค๋ ์ฐฐ๋ก๊ฐ์ด ์์๋ฃ๋๋ค.
6. ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์์ ์ซ์ ๊ฒ์ฆ
⇒ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์์ Query
์ ๊ฐ์ ์ญํ ์ Path
๊ฐ ํด์ค๋ค.
from typing import Union
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: Union[str, None] = Query(default=None, alias="item-query")
):
result = {"item_id": item_id}
if q:
result.update({"q": q})
return result
- ์ซ์ ๋ฒ์ ๊ฒ์ฆ
async def read_items(
item_id: int = Path(title="The ID of the item to get", gt=0, lt=1000),
q: Union[str, None] = Query(default=None, alias="item-query")
):
result = {"item_id": item_id}
if q:
result.update({"q": q})
return result
gt
= greater,lt
= less then,ge
= greater or equal,le
= less than or equal- ์ค์๋ ๊ฐ๋ฅ
'๐ฅ Web > โก Back-end | FastAPI' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[FastAPI] Tutorial(2) (0) | 2023.03.30 |
---|