FastAPI 3
출처 : FastAPI
🚫 아래 내용은 주관적인 생각이므로 사실과 다를 수 있습니다.
Request Body
- 클라이언트에서 API로 데이터를 전송할 때, request body로 보낸다
- API에서 클라이언트로 데이터를 전송할 때는 response body로 보낸다
- API는 대부분의 경우 response body를 보내야하지만, 클라이언트는 그렇지 않다
- request body를 선언하려면 Pydantic 모델들을 쓰면 된다
- HTTP GET 메소드로는 데이터를 보내지 않는것을 권장한다
Import Pydantic’s BaseModel
- 일단 pydantic에서 BaseModel을 임포트한다
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
Create your data model
- 그 후엔 임포트한 BaseModel을 상속받아 데이터 모델을 선언한다
- 필드 타입들은 파이썬 기본 타입들을 사용한다
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
- 쿼리 파라미터를 선언했을때와 같이,
모델 필드가 기본값을 가지면 비필수 항목이 된다 - 옵셔널로 선언하고 싶다면 기본값을 None으로 하면 된다
- 위의 모델은 JSON object나 파이썬의 dict 처럼 선언된다
{ "name": "Foo", "description": "An optional description", "price": 45.2, "tax": 3.5 }
- “description”과 “tax”는 기본값이 있으므로 아래처럼 생략해도 된다
{ "name": "Foo", "price": 45.2 }
Declare it as a parameter
- 선언한 데이터 모델은 패스/쿼리 파라미터와 같이 사용하면 된다 ```py from typing import Optional
from fastapi import FastAPI from pydantic import BaseModel
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None
app = FastAPI()
@app.post(“/items/”) async def create_item(item: Item): return item
### Results
- 이 정도 만으로도 FastAPI는 아래의 효과를 낸다
- request body를 JSON처럼 읽음
- 자동 형변환(필요시)
- 데이터 검증
- 데이터가 유효하지 않을 경우, 깔끔-명확한 에러를 반환
- 정확히 어디서 뭐가 잘못된건지 알려줌
- 파라미터로 받은 값을 보여줌
- 선언한 그대로의 모든 필드와 타입들을 에디터에서 지원함
- 모델의 JSON 스키마 생성, 프로젝트 어디에서든 사용 가능
- 그렇게 생성된 스키마들은 자동생성 API문서의 일부분으로 사용된다
### Automatic docs
- 모델 스키마들을 이용해 API문서를 자동으로 생성해줌
### Editor support
- Pydantic 모델 대신 dict 타입으로 받지만 않으면,
에디터 어디에서든 타입 자동완성 추천 목록으로 작동한다
- 추가로 타입별 연산자 오류 알림 또한 작동함
- 우연히 발생한게 아니고, 프레임워크 전반에 걸쳐 이런 디자인이 반영됨
### User the model
- 함수 안에서, 모델 객체의 모든 필드에 직접적인 접근이 가능함
```py
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
app = FastAPI()
@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 body + path parameters
- 패스 파라미터와 request body는 동시 사용이 가능하다
- FastAPI는 패스 파라미터의 값은 경로에서 읽어오고,
- Pydantic 모델들은 request body에서 읽어온다 ```py from typing import Optional
from fastapi import FastAPI from pydantic import BaseModel
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None
app = FastAPI()
@app.put(“/items/{item_id}”) async def create_item(item_id: int, item: Item): return {“item_id”: item_id, **item.dict()}
### Request body + path + query parameters
- 물론 request body, 패스/쿼리 파라미터 전부 동시 사용도 가능
- FastAPI가 적절한 곳에서 알아서 잘 읽어온다
```py
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
app = FastAPI()
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: Optional[str] = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
- 함수 파리미터는 다음과 같이 인식합니다
- 파라미터가 경로에도 선언되어 있는지 - 있다면 패스 파라미터
- 파라미터가 단수형 타입인지(int, float, str, bool, …) - 쿼리 파라미터
- 파라미터가 Pydantic 모델 타입으로 선언되어 있는지 - request body
Without Pydantic
- Pydantic 모델이 마음에 안들면 Body 파라미터를 써도 된다
Query Parameters and String Validations
- FastAPI는 파라미터의 추가적인 정보/타입 검증의 선언이 가능함 ```py from typing import Optional
from fastapi import FastAPI
app = FastAPI()
@app.get(“/items/”) async def read_items(q: Optional[str] = None): results = {“items”: [{“item_id”: “Foo”}, {“item_id”: “Bar”}]} if q: results.update({“q”: q}) return results
- 쿼리 파라미터 'q'는 Optional[str] 타입이다
- 'q'는 str 타입일 수도, None일 수도 있다
- 기본값은 None이다
- FastAPI는 'q'를 비필수로 처리한다
### Additional validation
- 'q'가 비필수 파라미터라도 입력이 된다면 길이를 50으로 제한할거다
#### Import Query
- 제한하려면 우선 fastapi의 Query를 임포트한다
```py
from typing import Optional
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Optional[str] = Query(None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Use Query as the default value
- 위와 같이 파라미터의 검증값을 설정할 때 Query를 사용하면,
- 기본값 또한 함께 설정이 가능하다(위의 경우 기본값 None)
q: Optional[str] = Query(None) # equals to -> q: Optional[str] = None
Add more validations
- 여러개의 검증값을 동시에 사용하는 것도 가능하다
@app.get("/items/") async def read_items(q: Optional[str] = Query(None, min_length=3, max_length=50)):
Add regular expressions
- 정규표현식도 가능
q: Optional[str] = Query(None, min_length=3, max_length=50, regex="^fixedquery$")
Default values
- None 외의 값도 기본값으로 설정 가능하다
q: str = Query("fixedquery", min_length=3)
- 기본값을 가진다는 것은 비필수 파라미터가 된다는 뜻이기도 하다
Make it required
- 필수 파라미터로 설정하려면 기본값을 설정하지 않으면 된다
- Query의 경우는 ‘…‘를 사용한다
q: str = Query(..., min_length=3)
Query parameter list / multiple values
- 아래와 같이 기재하면 쿼리 파라미터값을 명시적으로 다중/리스트로 지정할 수 있다
@app.get("/items/") async def read_items(q: Optional[List[str]] = Query(None)): query_items = {"q": q} return query_items
- 위 경우의 URL은 아래와 같다
http://localhost:8000/items/?q=foo&q=bar
- 함수 내부에서는 ‘q’가 리스트로 처리 되기 때문에 response는 아래와 같다
{ "q": [ "foo", "bar" ] }
- 위와 같이 리스트 타입의 쿼리 파라미터를 받고 싶다면
- 명시적으로 Query를 사용해줘야만 한다
- 그렇지 않으면 request body로 처리될 것이다
Query parameter list / multiple values with defaults
- 아래처럼 리스트로 받는 파라미터에도 기본값 설정이 가능하다
async def read_items(q: List[str] = Query(["foo", "bar"])):
Using list
- List[타입] 대신 list를 써도 된다
q: list = Query([])
- 그러나 달랑 list만 쓴다면 리스트 내 항목들의 타입 검증은 하지 않는다
Declare more metadata
- 파라미터에 대해 더 많은 정보를 기재할 수도 있다
- 해당 정보는 자동생성된 API 문서와 기타 외부 도구에서 사용된다
- OpenAPI 지원 레벨에 따라서 기재한 정보가 보이지 않을 수 있다
@app.get("/items/") async def read_items( q: Optional[str] = Query( None, title="Query string", # 파라미터 타이틀 description="Query string for the...", # 파라미터 설명 min_length=3, ) ): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) return results
Alias parameters
- 파라미터의 이름을 바꿔서 받을 수도 있다
@app.get("/items/") async def read_items(q: Optional[str] = Query(None, alias="item-query")): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) return results
- ‘alias’를 이용하면, ‘q’의 이름을 ‘item-query’로 바꿔 받을 수 있다
Deprecating parameters
- Query의 옵션에서 deprecated=True를 이용하면,
- 더 이상 사용하지 않는 파라미터를 표기할 수 있다
q: Optional[str] = Query(None, deprecated=True)
- 위와 같이 작성시 자동생성된 API 문서에서 deprecated 표식을 확인할 수 있다
Path Parameters and Numeric Validations
- 쿼리 파라미터에서 검증과 추가 정보를 선언했던것처럼
- 패스 파라미터에서도 가능하다
Import Path
- 우선 fastapi에서 Path를 임포트한다 ```py from typing import Optional
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: Optional[str] = Query(None, alias=”item-query”), ): results = {“item_id”: item_id} if q: results.update({“q”: q}) return results
### Declare metadata
- 추가 정보는 Query와 같은 방식으로 사용하면 된다
```py
item_id: int = Path(..., title="The ID of the item to get")
- 패스 파라미터는 경로의 일부분이기 때문에 항상 필요하다
- 기본값 자리에 ‘…’ 대신 None을 넣더라도, 바뀌는건 없다
- 패스 파라미터는 언제나 필수다
Order the parameters as you need
- 기본값이 있는 파라미터와 없는 파라미터를 동시에 써야한다면
- 아래와 같이 기본값이 없는 파라미터를 먼저 선언하도록 한다
from fastapi import FastAPI, Path app = FastAPI() @app.get("/items/{item_id}") async def read_items( q: str, item_id: int = Path(..., title="The ID of the item to get") ): results = {"item_id": item_id} if q: results.update({"q": q}) return results
Order the parameters as you need, tricks
- 원하는 순서대로 선언하고 싶다면 ‘*‘를 사용하자 ```py from fastapi import FastAPI, Path
app = FastAPI()
@app.get(“/items/{item_id}”) async def read_items( *, item_id: int = Path(…, title=”The ID of the item to get”), q: str ): results = {“item_id”: item_id} if q: results.update({“q”: q}) return results
- 위 처럼 '*'를 첫 파라미터로 준다면 기본값 유무와 순서가 상관없어진다
### Number validations
- Query와 Path 모두 아래와 같은 방식으로 숫자 검증이 가능하다
```py
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(..., title="The ID of the item to get", gt=0, le=1000),
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
Number validations: greater than or equal
- ‘ge’는 ~ 이상,
g
reater than ore
qual의 약자이다 - ‘gt’는 ~ 초과,
g
reatert
han의 약자이다
Number validations: greater than and less than or equal
- ‘le’는 ~ 이하,
l
ess than ore
qual의 약자이다 - ‘lt’는 ~ 미만,
l
esst
han의 약자이다
Number validations: floats, greater than and less than
- 아래와 같이 소수도 가능하다 ```py 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”, ge=0, le=1000), q: str, size: float = Query(…, gt=0, lt=10.5) ): results = {“item_id”: item_id} if q: results.update({“q”: q}) return results ```
댓글남기기