경로 처리 고급 구성¶
OpenAPI operationId¶
경고
OpenAPI “전문가”가 아니라면, 아마 이 내용은 필요하지 않을 것입니다.
매개변수 operation_id를 사용해 경로 처리에 사용할 OpenAPI operationId를 설정할 수 있습니다.
각 작업마다 고유하도록 보장해야 합니다.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", operation_id="some_specific_id_you_define")
async def read_items():
return [{"item_id": "Foo"}]
경로 처리 함수 이름을 operationId로 사용하기¶
API의 함수 이름을 operationId로 사용하고 싶다면, 모든 API를 순회하면서 APIRoute.name을 사용해 각 경로 처리의 operation_id를 덮어쓸 수 있습니다.
모든 경로 처리를 추가한 뒤에 수행해야 합니다.
from fastapi import FastAPI
from fastapi.routing import APIRoute
app = FastAPI()
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
def use_route_names_as_operation_ids(app: FastAPI) -> None:
"""
Simplify operation IDs so that generated API clients have simpler function
names.
Should be called only after all routes have been added.
"""
for route in app.routes:
if isinstance(route, APIRoute):
route.operation_id = route.name # in this case, 'read_items'
use_route_names_as_operation_ids(app)
팁
app.openapi()를 수동으로 호출한다면, 그 전에 operationId들을 업데이트해야 합니다.
경고
이렇게 할 경우, 각 경로 처리 함수의 이름이 고유하도록 보장해야 합니다.
서로 다른 모듈(파이썬 파일)에 있어도 마찬가지입니다.
OpenAPI에서 제외하기¶
생성된 OpenAPI 스키마(따라서 자동 문서화 시스템)에서 특정 경로 처리를 제외하려면, include_in_schema 매개변수를 False로 설정하세요:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", include_in_schema=False)
async def read_items():
return [{"item_id": "Foo"}]
docstring에서 고급 설명 가져오기¶
OpenAPI에 사용할 경로 처리 함수의 docstring 줄 수를 제한할 수 있습니다.
\f(이스케이프된 "form feed" 문자)를 추가하면 FastAPI는 이 지점에서 OpenAPI에 사용할 출력 내용을 잘라냅니다.
문서에는 표시되지 않지만, Sphinx 같은 다른 도구는 나머지 부분을 사용할 수 있습니다.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(item: Item):
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
\f
:param item: User input.
"""
return item
🤓 Other versions and variants
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(item: Item):
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
\f
:param item: User input.
"""
return item
추가 응답¶
경로 처리에 대해 response_model과 status_code를 선언하는 방법을 이미 보셨을 것입니다.
이는 경로 처리의 기본 응답에 대한 메타데이터를 정의합니다.
모델, 상태 코드 등과 함께 추가 응답도 선언할 수 있습니다.
이에 대한 문서의 전체 장이 있으니, OpenAPI의 추가 응답에서 읽어보세요.
OpenAPI Extra¶
애플리케이션에서 경로 처리를 선언하면, FastAPI는 OpenAPI 스키마에 포함될 해당 경로 처리의 관련 메타데이터를 자동으로 생성합니다.
기술 세부사항
OpenAPI 명세에서는 이를 Operation Object라고 부릅니다.
여기에는 경로 처리에 대한 모든 정보가 있으며, 자동 문서를 생성하는 데 사용됩니다.
tags, parameters, requestBody, responses 등이 포함됩니다.
이 경로 처리 전용 OpenAPI 스키마는 보통 FastAPI가 자동으로 생성하지만, 확장할 수도 있습니다.
openapi_extra 매개변수를 사용해 경로 처리의 OpenAPI 스키마를 확장할 수 있습니다.
OpenAPI 확장¶
예를 들어 openapi_extra는 OpenAPI Extensions를 선언하는 데 도움이 될 수 있습니다:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", openapi_extra={"x-aperture-labs-portal": "blue"})
async def read_items():
return [{"item_id": "portal-gun"}]
자동 API 문서를 열면, 해당 특정 경로 처리의 하단에 확장이 표시됩니다.

또한 API의 /openapi.json에서 결과 OpenAPI를 보면, 특정 경로 처리의 일부로 확장이 포함된 것도 확인할 수 있습니다:
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
사용자 정의 OpenAPI 경로 처리 스키마¶
openapi_extra의 딕셔너리는 경로 처리에 대해 자동으로 생성된 OpenAPI 스키마와 깊게 병합됩니다.
따라서 자동 생성된 스키마에 추가 데이터를 더할 수 있습니다.
예를 들어 Pydantic과 함께 FastAPI의 자동 기능을 사용하지 않고, 자체 코드로 요청을 읽고 검증하기로 결정할 수도 있지만, OpenAPI 스키마에는 여전히 그 요청을 정의하고 싶을 수 있습니다.
그럴 때 openapi_extra를 사용할 수 있습니다:
from fastapi import FastAPI, Request
app = FastAPI()
def magic_data_reader(raw_body: bytes):
return {
"size": len(raw_body),
"content": {
"name": "Maaaagic",
"price": 42,
"description": "Just kiddin', no magic here. ✨",
},
}
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {
"application/json": {
"schema": {
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"},
"description": {"type": "string"},
},
}
}
},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
data = magic_data_reader(raw_body)
return data
이 예시에서는 어떤 Pydantic 모델도 선언하지 않았습니다. 사실 요청 바디는 JSON으로 parsed되지도 않고, bytes로 직접 읽습니다. 그리고 함수 magic_data_reader()가 어떤 방식으로든 이를 파싱하는 역할을 담당합니다.
그럼에도 불구하고, 요청 바디에 대해 기대하는 스키마를 선언할 수 있습니다.
사용자 정의 OpenAPI 콘텐츠 타입¶
같은 트릭을 사용하면, Pydantic 모델을 이용해 JSON Schema를 정의하고 이를 경로 처리의 사용자 정의 OpenAPI 스키마 섹션에 포함시킬 수 있습니다.
요청의 데이터 타입이 JSON이 아니더라도 이렇게 할 수 있습니다.
예를 들어 이 애플리케이션에서는 Pydantic 모델에서 JSON Schema를 추출하는 FastAPI의 통합 기능도, JSON에 대한 자동 검증도 사용하지 않습니다. 실제로 요청 콘텐츠 타입을 JSON이 아니라 YAML로 선언합니다:
import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError
app = FastAPI()
class Item(BaseModel):
name: str
tags: list[str]
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
try:
data = yaml.safe_load(raw_body)
except yaml.YAMLError:
raise HTTPException(status_code=422, detail="Invalid YAML")
try:
item = Item.model_validate(data)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors(include_url=False))
return item
그럼에도 기본 통합 기능을 사용하지 않더라도, YAML로 받고자 하는 데이터에 대한 JSON Schema를 수동으로 생성하기 위해 Pydantic 모델을 여전히 사용합니다.
그 다음 요청을 직접 사용하고, 바디를 bytes로 추출합니다. 이는 FastAPI가 요청 페이로드를 JSON으로 파싱하려고 시도조차 하지 않는다는 뜻입니다.
그리고 코드에서 YAML 콘텐츠를 직접 파싱한 뒤, 다시 같은 Pydantic 모델을 사용해 YAML 콘텐츠를 검증합니다:
import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError
app = FastAPI()
class Item(BaseModel):
name: str
tags: list[str]
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
try:
data = yaml.safe_load(raw_body)
except yaml.YAMLError:
raise HTTPException(status_code=422, detail="Invalid YAML")
try:
item = Item.model_validate(data)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors(include_url=False))
return item
팁
여기서는 같은 Pydantic 모델을 재사용합니다.
하지만 마찬가지로, 다른 방식으로 검증할 수도 있습니다.