Join us Sept 17 at .local NYC! Use code WEB50 to save 50% on tickets. Learn more >
MongoDB Event
Docs Menu
Docs Home
/ / /
PyMongo 드라이버
/

튜토리얼: FastAPI 통합

FastAPI는 표준 Python 유형 힌트를 사용하여 API를 빌드하도록 설계된 최신 고성능 프로덕션용 비동기 Python 웹 프레임워크 입니다. 이 튜토리얼에서는 MongoDB 와 FastAPI를 통합하는 완전한 CRUD 애플리케이션 빌드 방법을 학습 수 있습니다.

FastAPI의 비동기 비차단 아키텍처를 사용하면 멀티스레딩에 의존하지 않고도 수천 개의 동시 요청을 처리하다 할 수 있습니다. Python의 asyncio 이벤트 루프 사용하여 코루틴을 통해 동시성을 관리 . 이를 통해 애플리케이션 쿼리가 반환되기를 기다리는 동안 요청 일시 중단할 수 있습니다. 이렇게 하면 메모리 사용량이 줄어들고 지연 시간 줄어들 수 있습니다.

FastAPI에는 다음 구성 요소를 포함하여 몇 가지 내장 통합 및 기능이 있습니다.

  • 자동 데이터 구문 분석, 유효성 검사 및 직렬화를 위한 Pydantic

  • 자동 생성된 대화형 API 문서를 위한 OpenAPI 및 JSON schema

  • 깔끔하고 테스트 가능한 코드를 위한 경량 Pythonic 종속성 주입 시스템

  • OAuth2 및 JSON web token 인증 및 권한 부여 부여 지원

이러한 기능은 상용구 코드를 최소화하여 개발을 간소화합니다. FastAPI의 기능에 대한 자세한 내용은 FastAPI 웹사이트 참조하세요.

이 튜토리얼의 완성된 샘플 앱 MongoDB with FastAPI 샘플 프로젝트 GitHub 리포지토리 에서 찾을 수 있습니다.

이 튜토리얼을 시작하기 전에 다음 구성 요소가 설치 및 설정하다 되어 있는지 확인하십시오.

  • Python v3.9 - 3.11

  • MongoDB Atlas cluster. 클러스터 설정하다 방법에 학습 자세한 내용은 시작하기 가이드 참조하세요.

종속성을 설치하고 MongoDB 에 연결한 다음 FastAPI 서버 시작합니다.

1

터미널에서 다음 명령을 실행하여 mongodb-pymongo-fastapi GitHub 리포지토리 에서 코드를 복제합니다.

git clone git@github.com:mongodb-developer/mongodb-pymongo-fastapi.git
cd mongodb-pymongo-fastapi
2

가상 환경 활성화

가상 환경에 Python 종속성을 설치하면 개별 프로젝트에 대한 라이브러리 버전을 설치할 수 있습니다.pip 명령을 실행 전에 virtualenv 가 활성화되어 있는지 확인합니다.

터미널에서 다음 명령을 실행하여 requirements.txt 파일 에 나열된 종속성을 설치합니다.

cd mongodb-pymongo-fastapi
pip install -r requirements.txt

종속성을 다운로드 하고 설치하는 데 몇 분 정도 걸릴 수 있습니다.

3

MongoDB Atlas 연결 문자열 찾기 가이드 에 따라 연결 문자열 조회 합니다.

터미널에서 다음 코드를 실행하여 연결 문자열 저장 환경 변수를 만듭니다.

export MONGODB_URL="mongodb+srv://<username>:<password>@<url>/<db>?retryWrites=true&w=majority"

환경 변수 재설정

새 터미널 세션을 시작할 때마다 이 환경 변수를 재설정해야 합니다.Direnv를 사용하면 이 프로세스 더 쉽게 수행할 수 있습니다.

예시 애플리케이션 의 모든 코드는 mongodb-pymongo-fastapi GitHub 리포지토리 의 앱.py 파일 에 저장됩니다.

이 코드를 사용하여 다음 작업을 수행합니다.

  1. MONGODB_URL 환경 변수와 함께 AsyncMongoClient() 메서드를 사용하고 college라는 데이터베이스 지정하여 MongoDB Atlas cluster 에 연결합니다.

  2. college 데이터베이스 에 대한 포인터를 만듭니다.

  3. students 컬렉션 에 대한 포인터 만들기

client = AsyncMongoClient(os.environ["MONGODB_URL"],server_api=pymongo.server_api.ServerApi(version="1", strict=True,deprecation_errors=True))
db = client.get_database("college")
student_collection = db.get_collection("students")

AsyncMongoClient() 호출에는 연결에서 Stable API 기능 활성화 하는 server_api 매개 변수가 포함되어 있습니다. 배포서버 버전이 5.0 이상인 MongoDB 서버를 사용하는지 확인합니다. 자세한 내용은 이 가이드의 Stable API 섹션을 참조하세요.

이 애플리케이션 에는 StudentModel, UpdateStudentModel, StudentCollection의 세 가지 모델이 있습니다. 이러한 모델은 app.py 파일 에 정의되어 있습니다.

애플리케이션 의 모든 모델은 BaseModel기본 유형 유효성 검사, JSON 구문 분석 및 직렬화, 기본 오류 처리를 제공하는 Pydantic 를 기반으로 빌드 .

StudentModel 대부분의 엔드포인트에 대한 응답 모델로 사용되는 프라이머리 모델입니다.

MongoDB _id 을 문서의 기본값 식별자로 사용합니다. 그러나 Pydantic에서 밑줄로 시작하는 필드 이름은 비공개 속성으로 취급되며 직접 값을 할당할 수 없습니다. 이 문제를 해결하기 위해 Pydantic 모델에서 필드 이름을 id 으로 지정할 수 있지만 MongoDB 에 올바르게 매핑되도록 별칭을 _id 로 지정합니다.

id 필드 값을 문자열로 강제 변환하는 BeforeValidator 주석이 달린 사용자 지정 유형인 PyObjectId 유형입니다. 이렇게 하면 MongoDB의 ObjectId 이 모델에서 올바르게 허용되고 직렬화될 수 있습니다.

이 설정 다음 model_config 옵션이 필요합니다.

  • populate_by_name=True: 필드 이름(id) 또는 해당 별칭(_id)을 사용하여 모델을 초기화할 수 있습니다.

  • arbitrary_types_allowed=True: 다음과 같은 사용자 지정 유형을 지원 합니다. PyObjectId

id 필드 도 기본값 이 None인 선택 사항으로 정의되어 있으므로 id을 지정하지 않고도 새 StudentModel 인스턴스 만들 수 있습니다. 문서 삽입되면 MongoDB 자동으로 _id 를 생성하고, 이 값은 API 응답으로 반환됩니다.

model_config 에는 FastAPI의 자동 생성 OpenAPI(Swagger) 문서에 사용되는 예시 데이터를 정의하는 json_schema_extra 설정도 포함되어 있습니다.

app.py 파일 의 다음 코드에서 StudentModel 정의를 볼 수 있습니다.

# Represents an ObjectId field in the database.
# It will be represented as a `str` on the model so that it can be serialized to JSON.
PyObjectId = Annotated[str, BeforeValidator(str)]
class StudentModel(BaseModel):
"""
Container for a single student record.
"""
# The primary key for the StudentModel, stored as a `str` on the instance.
# This will be aliased to ``_id`` when sent to MongoDB,
# but provided as ``id`` in the API requests and responses.
id: Optional[PyObjectId] = Field(alias="_id", default=None)
name: str = Field(...)
email: EmailStr = Field(...)
course: str = Field(...)
gpa: float = Field(..., le=4.0)
model_config = ConfigDict(
populate_by_name=True,
arbitrary_types_allowed=True,
json_schema_extra={
"example": {
"name": "Jane Doe",
"email": "jdoe@example.com",
"course": "Experiments, Science, and Fashion in Nanophotonics",
"gpa": 3.0,
}
},
)

UpdateStudentModel 에는 StudentModel과의 주요 차이점이 있습니다.

  • 수정할 수 없으므로 id 속성이 없습니다.

  • 모든 필드는 선택 사항이므로 업데이트 하려는 필드만 제공할 수 있습니다.

  • mongo_config 변수에는 json_encoders={ObjectId: str}가 포함됩니다.

FastAPI는 데이터를 JSON 문자열로 인코딩 및 디코딩하며, MongoDB의 BSON 데이터 유형 저장 수 있는 모든 데이터 유형을 지원 하지는 않습니다. BSON 기본값 UUID 속성인 _id에 사용되는 ObjectId 을 포함하여 더 많은 비 JSON 네이티브 데이터 유형을 지원 합니다. 이 때문에 ObjectId 객체를 _id 필드 에 저장하기 전에 문자열로 변환해야 합니다. json_encoders 설정은 Pydantic에 이 작업을 수행하도록 지시합니다.

BSON JSON 과 비교하는 방법에 대한 자세한 내용은 JSON 및 BSON MongoDB 문서를 참조하세요.

app.py 파일 의 다음 코드에서 UpdateStudentModel 정의를 볼 수 있습니다.

class UpdateStudentModel(BaseModel):
"""
A set of optional updates to be made to a document in the database.
"""
name: Optional[str] = None
email: Optional[EmailStr] = None
course: Optional[str] = None
gpa: Optional[float] = None
model_config = ConfigDict(
arbitrary_types_allowed=True,
json_encoders={ObjectId: str},
json_schema_extra={
"example": {
"name": "Jane Doe",
"email": "jdoe@example.com",
"course": "Experiments, Science, and Fashion in anophotonics",
"gpa": 3.0,
}
},
)

StudentCollection 클래스는 인스턴스 목록을 캡슐화하도록 정의되어 StudentModel 있습니다. 이 엔드포인트의 목적은 악의적인 사용자가 브라우저가 JSON 배열을 처리하다 방식을 악용하여 민감한 데이터에 액세스 시도하는 JSON 하이재킹으로부터 일부 보호 기능을 제공하는 것입니다. 자세한 내용은 Haacked 웹사이트 에서 JSON 하이재킹 문서를 참조하세요.

app.py 파일 의 다음 코드에서 StudentCollection 정의를 볼 수 있습니다.

class StudentCollection(BaseModel):
"""
A container holding a list of `StudentModel` instances
"""
students: List[StudentModel]

다음 표에서는 이 애플리케이션 에 정의된 애플리케이션 경로에 대해 설명합니다.

루트
작업

POST /students/

새 학생 만들기

GET /students/

모든 학생 목록 보기

GET /students/{id}

단일 학생 보기

PUT /students/{id}

학생 업데이트

DELETE /students/{id}

학생 삭제

이러한 경로는 다음 섹션에 설명된 대로 app.py 파일 에 정의되어 있습니다.

create_student 경로는 POST 요청 에서 새 학생 데이터를 JSON 문자열로 수신합니다. JSON 요청 본문을 Python 사전으로 디코딩한 다음 MongoDB 클라이언트 에 전달합니다.

insert_one 메서드 응답에는 새로 생성된 학생의 _id 가 포함되며, 이 엔드포인트가 post 데코레이터 호출에서 response_model_by_alias=False 을(를) 지정하기 때문에 id (으)로 제공됩니다. 새 학생을 삽입한 후 이 메서드는 inserted_id 를 사용하여 올바른 문서 찾고 이를 JSONResponse에 반환합니다.

FastAPI는 기본값 으로 HTTP 200 상태 코드를 반환하지만, 이 경로는 학생이 생성되었음을 명시적으로 나타내기 위해 201 ('생성됨')을 반환합니다.

app.py 파일 의 다음 코드에서 create_student 정의를 볼 수 있습니다.

@app.post(
"/students/",
response_description="Add new student",
response_model=StudentModel,
status_code=status.HTTP_201_CREATED,
response_model_by_alias=False,
)
async def create_student(student: StudentModel = Body(...)):
"""
Insert a new student record.
A unique ``id`` will be created and provided in the response.
"""
new_student = student.model_dump(by_alias=True, exclude=["id"])
result = await student_collection.insert_one(new_student)
new_student["_id"] = result.inserted_id
return new_student

애플리케이션 에는 모든 학생을 보는 경로와 id(으)로 지정된 개별 학생을 보는 경로가 있습니다.

app.py 파일 의 다음 코드에서 list_students 정의를 볼 수 있습니다.

@app.get(
"/students/",
response_description="List all students",
response_model=StudentCollection,
response_model_by_alias=False,
)
async def list_students():
"""
List all the student data in the database.
The response is unpaginated and limited to 1000 results.
"""
return StudentCollection(students=await student_collection.find().to_list(1000))

참고

결과 페이지 매김

이 예시 메서드를 to_list() 사용합니다. 그러나 실제 애플리케이션 에서는 의 건너뛰기 및 제한 매개변수를 사용하여 find 결과를 페이지 매김하는 것이 좋습니다.

학생 세부 정보 경로에는 FastAPI가 show_student 함수에 인수로 전달하는 id 경로 매개 변수가 있습니다. id 를 사용하여 데이터베이스 에서 해당 학생을 찾으려고 시도합니다.

지정된 id 가 있는 문서 존재하지 않으면 HTTPException 상태가 404인 문서가 발생합니다.

app.py 파일 의 다음 코드에서 show_students 정의를 볼 수 있습니다.

@app.get(
"/students/{id}",
response_description="Get a single student",
response_model=StudentModel,
response_model_by_alias=False,
)
async def show_student(id: str):
"""
Get the record for a specific student, looked up by ``id``.
"""
if (
student := await student_collection.find_one({"_id": ObjectId(id)})
) is not None:
return student
raise HTTPException(status_code=404, detail="Student {id} not found")

update_student 경로는 create_studentshow_student 경로의 조합과 유사하게 작동합니다. 업데이트 할 학생의 id 와 JSON 본문의 새 데이터를 수신합니다.

이 경로는 수신된 데이터의 모든 매개변수를 반복하고 제공된 매개변수만 수정합니다.find_one_and_update() to $ 설정하다 메서드를 사용합니다.

업데이트 할 필드가 없는 경우 원본 StudentModel 문서 반환합니다. 업데이트 하거나 반환할 일치하는 문서 찾을 수 없으면 404 오류가 발생합니다.

app.py 파일 의 다음 코드에서 update_student 정의를 볼 수 있습니다.

@app.put(
"/students/{id}",
response_description="Update a student",
response_model=StudentModel,
response_model_by_alias=False,
)
async def update_student(id: str, student: UpdateStudentModel = Body(...)):
"""
Update individual fields of an existing student record.
Only the provided fields will be updated.
Any missing or `null` fields will be ignored.
"""
student = {
k: v for k, v in student.model_dump(by_alias=True).items() if v is not None
}
if len(student) >= 1:
update_result = await student_collection.find_one_and_update(
{"_id": ObjectId(id)},
{"$set": student},
return_document=ReturnDocument.AFTER,
)
if update_result is not None:
return update_result
else:
raise HTTPException(status_code=404, detail=f"Student {id} not found")
# The update is empty, so return the matching document:
if (existing_student := await student_collection.find_one({"_id": ObjectId(id)})) is not None:
return existing_student
raise HTTPException(status_code=404, detail=f"Student {id} not found")

delete_student 는 단일 문서 에서 작동하므로 URL 에 id 를 제공해야 합니다. 일치하는 문서 찾아서 성공적으로 삭제하면 HTTP 상태 204 ('콘텐츠 없음')을 반환하고 문서 반환하지 않습니다. 지정된 id를 가진 학생을 찾을 수 없는 경우 404 오류를 반환합니다.

app.py 파일 의 다음 코드에서 delete_student 정의를 볼 수 있습니다.

@app.delete("/students/{id}", response_description="Delete a student")
async def delete_student(id: str):
"""
Remove a single student record from the database.
"""
delete_result = await student_collection.delete_one({"_id": ObjectId(id)})
if delete_result.deleted_count == 1:
return Response(status_code=status.HTTP_204_NO_CONTENT)
raise HTTPException(status_code=404, detail=f"Student {id} not found")

이제 애플리케이션 의 작동 방식을 이해했으므로 엔드포인트 사용을 시작할 수 있습니다. 엔드포인트에 요청을 보내고 결과를 확인하려면 다음 단계를 따르세요.

1

터미널에서 다음 코드를 실행하여 FastAPI 서버 시작합니다.

uvicorn app:app --reload
2

http://:127.0.0.1 8000/docs 로 이동합니다. 다음 이미지는 FastAPI 서버 에서 생성된 인터페이스를 보여줍니다.

브라우저 및 Swagger UI 스크린샷

요청 보내려는 엔드포인트를 클릭하고 필요한 매개변수를 입력한 다음 Execute을 클릭합니다. Show Student 엔드포인트에서 StudentModel.model_config 변수에 정의된 샘플 데이터를 볼 수 있습니다.

curl을 사용하여 API 테스트

명령줄 사용을 선호하는 경우 다음과 같은 curl 명령을 사용하여 요청 보낼 수 있습니다.

curl -X POST "http://127.0.0.1:8000/students/" \
-H "Content-Type: application/json" \
-d '{"name": "Jane Doe", "email": "jdoe@example.com", "course": "Physics", "gpa": 3.8}'
3

Atlas 에서 college 데이터베이스 로 이동하여 성공적인 POST, PUTDELETE 요청의 결과를 확인할 수 있습니다. List Students 엔드포인트를 사용하여 결과를 볼 수도 있습니다.

이제 FastAPI가 MongoDB 및 PyMongo 비동기 운전자 와 통합되는 방식에 대한 기본 사항을 이해했으므로 더 많은 기능을 추가할 수 있습니다. 다음 목록에는 이 애플리케이션 에 추가할 수 있는 방법에 대한 몇 가지 제안 사항이 포함되어 있습니다.

다음 섹션에서는 FastAPI를 MongoDB 및 PyMongo 비동기 운전자 와 통합하기 위한 몇 가지 권장사항 설명합니다.

민감한 자격 증명 애플리케이션 코드에 직접 저장하면 버전 제어 시스템에 우발적으로 노출될 수 있습니다. 대신 환경 변수를 사용하여 연결 문자열, API 키 및 기타 민감한 정보를 저장 .

개발 환경의 경우 다음 도구를 사용하여 환경 변수를 관리 할 수 있습니다.

  • direnv : 디렉토리 에 들어갈 때 파일에서 환경 변수를 자동으로 로드합니다..envrc

  • python-dotenv : Python 애플리케이션 내의 파일에서 환경 변수를 로드합니다..env

  • envdir: 디렉토리 의 파일에서 환경 변수를 설정합니다.

  • Honcho: 구성 파일에서 환경 변수를 로드합니다.

어떤 환경 변수 제어 도구를 선택하든 시크릿이 포함된 모든 파일(예: .env, .envrc 등)을 .gitignore 파일 에 추가하여 버전 제어에 커밋되지 않도록 합니다.

프로덕션 배포의 경우 키 관리 시스템(KMS)을 사용합니다. 개발 환경에서 KMS 솔루션을 사용하여 보안을 강화할 수도 있습니다.

FastAPI 애플리케이션 스타트업 및 종료 이벤트에 대한 응답으로 클라이언트 연결을 초기화하고 닫아 MongoDB 연결 수명 주기를 올바르게 관리 . 이 접근 방식은 모듈 수준에서 연결을 초기화하는 것보다 더 강력합니다.

MongoDB 클라이언트 FastAPI 애플리케이션 객체 에 연결하여 코드베이스 전체의 모든 경로 작업 함수에서 액세스할 수 있도록 합니다. 이렇게 하면 전역 변수가 필요하지 않고 종속성 관리 간소화됩니다.

FastAPI의 수명 이벤트를 사용하여 연결 관리 처리하다 .

import os
from contextlib import asynccontextmanager
from logging import info
from fastapi import FastAPI
from pymongo import AsyncMongoClient
@asynccontextmanager
async def db_lifespan(app: FastAPI):
# Startup
app.mongodb_client = AsyncMongoClient(os.environ["MONGODB_URL"])
app.database = app.mongodb_client.get_default_database()
ping_response = await app.database.command("ping")
if int(ping_response["ok"]) != 1:
raise Exception("Problem connecting to database cluster.")
else:
info("Connected to database cluster.")
yield
# Shutdown
await app.mongodb_client.close()
app: FastAPI = FastAPI(lifespan=db_lifespan)

MongoDB 에 JSON 데이터를 제공하는 FastAPI 엔드포인트에는 JSON 에서 기본적으로 지원되지 않는 ObjectIdBinary 유형이 포함될 수 있습니다.

여러 가지 방법으로 ObjectId 를 직렬화할 수 있습니다.

  • 문자열 기반 ID 사용: _id 필드에 대해 ObjectId 을 JSON 호환 문자열 값으로 바꿉니다. 이 접근 방식은 새 애플리케이션에는 적합할 수 있지만 기존 데이터가 있는 경우에는 적합하지 않을 수 있습니다.

  • 자동 변환: ObjectId 값을 JSON 으로 직렬화할 때 문자열 표현으로 변환하고, 수신 데이터를 처리 때 문자열을 다시 ObjectId 로 구문 분석합니다.

  • Pydantic 통합: 이 튜토리얼의 PyObjectId 유형에 설명된 대로 Pydantic의 유형 주석을 사용하여 자동 변환을 처리하다 .

  • 객체문서 매퍼(ODM): Beanie와 같은 ODM을 사용하여 필드가 기본값 _id 으로 를 ObjectId 사용한다고 가정하고 직렬화 및 역직렬화를 자동으로 처리합니다.

유연성을 유지하려면 데이터베이스 스키마 API 스키마 와 분리하세요. 이를 통해 API 인터페이스를 변경하지 않고도 MongoDB 문서 구조를 수정할 수 있습니다.

경로 작업에 Pydantic 클래스를 response_model 으로 제공하여 반환된 BSON 데이터의 필드를 변환, 유효성 검사, 문서 및 필터하다 . 다음 코드 예시 이 기술을 구현 방법을 보여 줍니다.

# A Pydantic class modelling the *response* schema.
class Profile(BaseModel):
"""
A profile for a single user.
"""
id: Optional[str] = Field(
default=None, description="MongoDB document ObjectID", alias="_id"
)
username: str
residence: str
current_location: List[float]
# A path operation that returns a Profile object as JSON.
app = FastAPI()
@app.get(
"/profiles/{profile_id}",
response_model=Profile, # Tells FastAPI that the returned object must match the Profile schema.
)
async def get_profile(profile_id: str) -> Mapping[str, Any]:
# Uses response_model to automatically convert, validate, and document
# the returned dict without manual Profile object creation.
profile = await app.profiles.find_one({"_id": profile_id})
if profile is not None:
# Return BSON document. FastAPI converts it automatically.
return profile
else:
raise HTTPException(
status_code=404, detail=f"No profile with id '{profile_id}'"
)

FastAPI 통합에 대한 자세한 내용은 다음 리소스를 참조하세요.

지원 받거나 MongoDB Community 에 기여하려면 MongoDB 개발자 커뮤니티를 참조하세요.

돌아가기

튜토리얼: Flask와 Celery 통합