🐥

Pymongo+Unique Indexによるユニークなユーザ管理

2024/01/06に公開

はい、学生エンジニアのMasamichiです。最近YouTubeで「溝部ひかる」にハマってます。この人の動画を見るためだけにTikTokを入れようか検討してます。

さて、ユーザをデータベースで管理する上で、同じユーザが重複することはあまり好ましくない場合が多いです。そこで今回はMongoDBのUnique Indexというものを使ってユーザが2回目以降に登録の操作を行った場合に、代わりにログインの処理を行うという実装をしてみます。

全体像

技術スタック

使用した技術は以下のとおりです。
My Skills

  • Python: 使用言語
  • FastAPI: バックエンドフレームワーク
  • GraphQL API: API
  • MongoDB: データベース

処理の流れ

処理の流れは下図のようになります:

簡単な説明をすると、RegisterMutationで新規登録を試みます。PythonがMongoDBにMutationで持たせたデータを追加しようとすると、MongoDB側がuser_idがCollection(RDBで言うTableのこと)内に同じuser_idが存在するかチェックします。後にMongoDBのレスポンスに応じて処理を切り替えるという仕組みなっています。

GraphQLではステータスコードの値による処理の決定ができないので、具体的なエラーを決め打ちしてエラー処理を考えます。

(詳細)
https://graphqlite.thecodingmachine.io/docs/error-handling
https://www.apollographql.com/docs/apollo-server/data/errors/

アウトプットイメージ

Register(成功)

user_idが存在しない場合、上図のように登録された内容と、Registerに成功したというメッセージがレスポンスとして返します。

Register(失敗:ログイン)

RegisterMutationを飛ばしたあと、もう一度リクエストを飛ばすと上図のように既に登録済みだというエラーレスポンスを返します。(エラーを返した場合の具体的なログイン処理は記載しない)

実装

Atlas UIでUnique Indexを設定

MongoDB AtlasというMongoDBのクラウドを利用します。[Database > Browse Collections > UserTable > Indexs]でユーザ管理をしているコレクションのインデックス項目へアクセスします。CREATE INDEXをクリックして画像のようにユーザIDにunique indexを付与します。

indexは以下のように1なら昇順、-1なら降順で付与されます。用途に合わせて利用してください。

json
{user_id: 1} //昇順でインデックスを付与
{user_id: -1} //降順でインデックスを付与

Regsiterの処理

Registerの処理をPythonで書きます。GraphQL APIがわからない方は以下の記事がオススメです。
https://zenn.dev/nameless_sn/articles/gql_fastapi_tutorial

スキーマ

FastAPIでGraphQL APIを使用する際は以下のようにスキーマ型の定義が必要です。

schemas.py
@strawberry.input # Mutation
class Register:
    user_id: str
    user_name: str
    avatar_url: str
    
@strawberry.type # Query
class Response:
    user_id: str
    user_name: str
    avatar_url: str

regsiter関数

Mutationでregister関数を定義します。

main.py
import pymongo #PythonでMongoDBを動かすためのパッケージ
#(省略)
mongo_client = MongoClient(os.gentenv("MONGO_URL"))
collection_user = mongo_client["Database"]["UserCollection"]

class Mutation:
    @strawberry.field
    def register(self, regist: Regsiter) -> Response:
    try:
        collection_user.insert_one(regsit.__dict__)
	return regist
    except pymongo.errors.DuplicateKeyError:
        raise Exception("You are already registered. Please Login")
    except Exception as e:
        raise {"message": str(e)} 

insertmethodでMongoDBへデータの追加をtryで実行します。既にコレクション内に同じuser_idが存在する場合は、PymongoがDuplicateKeyError、つまりUniqueでないキーが存在するという旨のエラーを出力します。従ってこのエラーに対する処理としてログインの処理を追加すれば良いです。

https://pymongo.readthedocs.io/en/stable/api/pymongo/errors.html

終わりに

Unique Indexを導入することで、ユニークユーザを簡単に実装することができます。前述した通りユーザ毎に一つしかアカウントを登録できないという処理はコンテンツにおいてかなり重宝されます。ぜひ活用してみてください。

Discussion