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 Webフレームワークです。このチュートリアルでは、 MongoDBと FastAPI を統合した完全なCRUDアプリケーションを構築する方法を学びます。

FastAPI の非同期、非ブロッキング アーキテクチャにより、マルチスレッドに依存せずに数千の同時リクエストを処理できます。Python の asyncioイベントループを使用し、コルーチンで同時実行性を管理します。これにより、アプリケーションはクエリが返されるのを待っている間、リクエストを一時停止することができます。これによりメモリ使用量が削減され、レイテンシを削減できます。

FastAPI には、次のコンポーネントを含むいくつかの組み込み統合と機能があります。

  • 自動データ解析、検証、直列化のためのPython

  • 自動生成されインタラクティブAPIドキュメントのための OpenAPI とJSON schema

  • クリーンでテスト可能なコードのための軽量、Python 依存関係インジェクション システム

  • OAuth2 およびJSON web token の認証と認可のサポート

これらの機能により、開発を簡素化するために単数形のコードを最小限に抑えることができます。 FastAPI の機能の詳細については、 FastAPI のウェブサイト を参照してください。

このチュートリアルの完了したサンプルアプリは、 FastAPI を使用したMongoDBサンプルプロジェクトGitHubリポジトリにあります。

このチュートリアルを開始する前に、次のコンポーネントがインストールされ、設定されていることを確認してください。

  • Python v3.9 - 3.11

  • MongoDB Atlasクラスター。クラスターのセットアップ方法について詳しくは、スタートガイドをご覧ください。

依存関係をインストールし、 MongoDBに接続して、 FastAPIサーバーを起動します 。

1

ターミナルで次のコマンドを実行して、 mongodb-pymongo-fastapi GitHubリポジトリからコードを複製します。

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

Tip

仮想環境をアクティブ化する

Python依存関係を Atlas App Services がインストールすると、個々のプロジェクト向けにライブラリのバージョンをインストールできます。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"

Tip

環境変数をリセットする

新しいターミナルセッションを開始するたびに、この環境変数をリセットする必要があります。このプロセスを簡素化するために directv を使用することができます。

例アプリケーションのすべてのコードは、 mongodb-pymongo-fastapi GitHubリポジトリのアプリ.pyファイルに保存されます。

このコードを使用して、次のアクションを実行します。

  1. MongoDB Atlasクラスターに接続するには、AsyncMongoClient() メソッドで MONGODB_URL 環境変数を使用し、college という名前のデータベースを指定します。

  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」セクションを参照してください。

このアプリケーションには、StudentModelUpdateStudentModelStudentCollection の 3 つのモデルがあります。これらのモデルは app.pyファイルで定義されています。

アプリケーション内のすべてのモデルは、基本的な型の検証、 JSON の解析と直列化、基本的なエラー処理を提供する Pytany 上で構築されます。BaseModel

StudentModel は、過半数のエンドポイントの応答モデルとして使用されるプライマリ モデルです。

MongoDB、ドキュメントのデフォルトの識別子として _id が使用されます。ただし、Python では、アンダースコアで始まるフィールド名はプライベート属性として扱われ、値を直接割り当てることはできません。これを回避するには、Python モデルでフィールドid に名前を付けますが、_id のエイリアスを指定することで、 MongoDBに正しくマッピングされます。

idフィールドのタイプは PyObjectId で、値を string に強制する BeforeValidator で注釈が付けられたカスタムタイプです。これにより、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(Swaggre)ドキュメントで使用される例データを定義する json_schema_extra 設定も含まれています。

StudentModel の定義は、app.pyファイル内の次のコードで確認できます。

# 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,
}
},
)

UpdateStudentModelStudentModel とは次の重要な違いがあります。

  • 変更できないため、id 属性はありません

  • すべてのフィールドは任意であるため、更新するフィールドのみを指定できます

  • mongo_config 変数には json_encoders={ObjectId: str} が含まれます

FastAPI はJSON string としてデータをエンコードおよびデコードします。これは MongoDB のBSONデータ型で保存できるすべてのデータ型をサポートしていません。BSON は、デフォルトのUUID 属性、_id に使用される ObjectId など、より多くのJSONネイティブ データ型をサポートしています。このため、ObjectId オブジェクトは _idフィールドに保存する前に string に変換する必要があります。json_encoders の設定は Pydentic にこれを行うよう指示します。

BSONとJSONの比較の詳細については、 JSONとBSON MongoDB記事を参照してください。

UpdateStudentModel の定義は、app.pyファイル内の次のコードで確認できます。

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 Hijacking に関する記事 をお読みください。

StudentCollection の定義は、app.pyファイル内の次のコードで確認できます。

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

次の表は、このアプリケーションで定義されているアプリケーションルートを説明しています。

route
アクション

POST /students/

新しい学生を作成する

GET /students/

すべての学生のリストを表示

GET /students/{id}

単一の学生を表示

PUT /students/{id}

学生を更新する

DELETE /students/{id}

学生を削除する

これらのルートは、次のセクションで説明されているように、app.pyファイルで定義されています。

create_student ルートは、 POSTリクエストで新しい学生データをJSON string として受け取ります。JSONリクエスト本体をPython辞書にデコードし、それをMongoDBクライアントに渡します。

insert_one メソッドの応答には、新しく作成された学生の _id が含まれます。このエンドポイントは post 修飾子呼び出しで response_model_by_alias=False を指定しているため、id として提供されます。新しい学生を挿入した後、メソッドは inserted_id を使用して正しいドキュメントを検索し、これを JSONResponse に返します。

FastAPI はデフォルトでHTTP 200 ステータス コードを返しますが、このルートは学生が作成されたことを明示的に示す 201(「作成済み」)を返します。

create_student の定義は、app.pyファイル内の次のコードで確認できます。

@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

このアプリケーションでは、すべての学生を表示するための 1 つのルートと、id で指定された個々の学生を表示するための 1 つのルートがあります。

list_students の定義は、app.pyファイル内の次のコードで確認できます。

@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() メソッドを使用します。ただし、実際のアプリケーションでは、結果をページ分割するために の skip と limitfind のパラメーターを使用することをお勧めします。

学生詳細ルートには id の path パラメーターがあり、FastAPI はこれを引数として show_student 関数に渡します。は、id を使用して、データベース内の対応する学生を検索しようとします。

指定された id のドキュメントが存在しない場合は、404 のステータスを持つ HTTPException が発生します。

show_students の定義は、app.pyファイル内の次のコードで確認できます。

@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_student ルートと show_student ルートの組み合わせと同様に機能します。更新する学生の id とJSON本文の新しいデータを受け取ります。

このルートは、受信したデータ内のすべてのパラメーターを反復処理し、提供されたパラメーターのみを変更します。 find_one_and_update() $set メソッドに使用します。

更新するフィールドがない場合は、元の StudentModelドキュメントが返されます。更新または返す一致するドキュメントが見つからない場合は、404 エラーが発生します。

update_student の定義は、app.pyファイル内の次のコードで確認できます。

@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 エラーが返されます。

delete_student の定義は、app.pyファイル内の次のコードで確認できます。

@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サーバーによって生成されたインターフェースを示しています。

ブラウザとスワガーUIのスクリーンショット

リクエストを送信するエンドポイントのをクリックし、必要なパラメータを入力して、Execute をクリックします。Show Student エンドポイントでは、StudentModel.model_config 変数で定義されているサンプルデータを確認できます。

Tip

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データベースに移動すると、POSTPUTDELETE リクエストの成功を確認できます。また、List Students エンドポイントを使用して結果を表示することもできます。

FastAPI がMongoDBおよびPyMongo Async ドライバーと統合する方法の基本が理解できたため、機能を追加できます。次のリストには、このアプリケーションに を追加する方法に関する提案がいくつか含まれています。

次のセクションでは、FastAPI をMongoDBおよびPyMongo Async ドライバーと統合するためのいくつかのベストプラクティスについて説明します。

アプリケーションコードで機密性の高い認証情報を直接保存すると、バージョン管理システムで誤って露出される可能性があります。代わりに、環境変数を使用して、接続文字列、 APIキー、その他の機密情報を保存します。

開発環境では、次のツールを使用して環境変数を管理できます。

  • directv :ディレクトリの入力時に ファイルから環境変数を自動的に読み込みます.envrc

  • python-dotenv :.env Pythonアプリケーション内の ファイルから環境変数を読み込みます

  • envdir :ディレクトリ内のファイルから環境変数を設定します

  • サーバー選択 : 構成ファイルから環境変数をロード

どの環境変数制御ツールを選択するかにかかわらず、シークレットを含むファイル(.env.envrcなど)を .gitignoreファイルに追加して、バージョン管理にコミットされるのを防ぎます。

本番環境には、 KMS(Key Management System) を使用します。また、開発環境で 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でネイティブにサポートされていない ObjectId 型と Binary 型が含まれる場合があります。

ObjectId は複数の方法で直列化できます。

  • 文字列ベースの ID を使用する : _id フィールドでは、ObjectId をJSON互換の文字列値に置き換えます。このアプローチは新しいアプリケーションには適していますが、既存のデータがある場合には適していない可能性があります。

  • 自動変換 : JSONに直列化するときに ObjectId 値を string 表現に変換し、受信データを処理するときに string を ObjectId に解析します。

  • ピリオド統合 : このチュートリアルの PyObjectId 型に示されているように、Pydative の 型注釈 を使用して自動変換を処理します。

  • オブジェクト ドキュメント マッパー(ODM) : Builder のような ODM_id ObjectIdを使用します。これでは、 フィールドでデフォルトで を使用することを想定しており、直列化と逆直列化を自動的に処理します。

柔軟性を維持するために、データベーススキーマをAPIスキーマから分離します。これにより、 APIインターフェイスを変更せずにMongoDBドキュメント構造を変更できます。

返されたBSONデータのフィールドを変換、検証、ドキュメント、フィルタリングするために、パス操作に Pythonクラスをresponse_model として指定します。次のコード例は、この手法を実装する方法を示しています。

# 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 Developer Community を参照してください。

戻る

チュートリアル:Flask と Celery の統合