FastAPIInstallationHello World(Path parameter, Query parameter)Bool type Query parameterReDoc, Swagger UIRequest Body, Typing파이썬 타입 정리PydanticEnumReference
FastAPI
Installation
pip install "fastapi[standard]"
Hello World(Path parameter, Query parameter)
from typing import Union from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"Hello": "World"} @app.get("/items/{item_id}") def read_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id, "q": q}
동일 메소드와 경로가 있을 경우, 위에 있는 것이 먼저 매칭된다.
그렇기에 /items/my 를 추가한다면 /items/{item_id} 경로 이전에 배치해야 한다.
path parameters가 아닌 매게변수가 있을 때 FastAPI는 이를 자동으로 query parameters로 간주한다.
fastapi dev main.py # 또는 uvicorn main:app --reload
Bool type Query parameter
@app.get("/items/{item_id}") async def read_item(item_id: str, q: str | None = None, short: bool = False): item = {"item_id": item_id} if q: item.update({"q": q}) if not short: item.update( {"description": "This is an amazing item that has a long description"} ) return item
Bool 타입에 1, True, true, on, yes 등의 값을 주면
True
로 인식한다.ReDoc, Swagger UI
Request Body, Typing
FastAPI에서는 Pydantic의 BaseModel을 상속한 객체가 매게변수로 지정되면, 이를 request body로 인식한다. 그럼 파이썬 타입을 사용해 body를 정의해보자.
from typing import Union from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str price: float is_offer: Union[bool, None] = None @app.put("/items/{item_id}") def update_item(item_id: int, item: Item): return {"item_name": item.name, "item_id": item_id}
- 데이터 검증
- 자동으로 데이터를 검증하고, 유효하지 않은 데이터의 경우 오류를 발생시킨다
- nested JSON objects의 검증도 가능하다
- 변환
- JSON, Path parameters, Query parameters, Cookies, Headers, Forms, Files 등을 모두 자동 변환해준다
- output type
- 자동 문서화
- 자동으로 ReDoc과 Swagger UI로 문서화 해준다
Pydantic을 쓰지 않고도
fastapi.Body
를 사용해서 body를 정의해줄 수 있다.파이썬 타입 정리
from typing import Dict, List, Set, Tuple, Optional # _old는 Python 3.9 이전의 방식. typing 라이브러리를 주로 이용했다. i: int = 1 f: float = 1.2 s: str = "문자열" b: bool = True by: bytes = # TODO: bytes 추가 ls: list[str] = ["문자열1", "문자열2"] # python 3.9+ ls_old: List[str] = ["문자열1", "문자열2"] t: tuple[int, int, str] = (1, 2, "3") t_old: Tuple[int, int, str] = (1, 2, "3") st: set[str] = set("1", "2") st_old: Set[str] = set("1", "2") d: dict[str, float] = {"1": 1.2} d_old: Dict[str, float] = {"1": 1.2} u: int | str = 1 u_old: Union[int, str] = "1" o: str | None = None o_old: Optional[str] = None o_old2: Union[str, None] = None # Optional 대신 이것을 권장. 왜냐면 보통은 실제로 Optional 하다는 의미가 아닌 None이 될 수 있다라는 의미이기에 # 함수 매게변수, 리턴 타입 정의 def func_ret_int(i: int) -> int: ... # 클래스를 타입으로 class Person: def __init__(self, name: str): self.name = name def get_person_name(one_person: Person): return one_person.name
Pydantic
Enum
from enum import Enum class ModelName(str, Enum): alexnet = "alexnet" resnet = "resnet" lenet = "lenet" @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"}