Prismaの共通化:アプリ間の連携を円滑に
はじめに
新卒2年目!まだまだ手探り状態ですが、AIエンジニアを目指す、ねもつです。
最近はAIを使ってストーリーを生成するチャットシステムの開発を行っています。
このプロジェクトでは、ユーザー側のアプリケーションとしてNext.jsを使用し、アカウント管理を行っており、AIを使用するためにFastAPIでAPIサーバーを構築しています。
その開発で、チャットの履歴を保存する必要がありましたが、AIとチャットのやりとりをするFastAPIではデータベースの処理を行っていませんでした。
そこで、Next.jsで使用している「prisma」について調べてみるとPythonでも使用できることを見つけました!
Prismaを使えばAIに問い合わせを行っているFastAPI側でも同じ仕組みでデータベースにチャットの履歴を保存・共有できる!と思い開発を進めてみました。
FastAPIにも同じような設定を書けば問題ない!と思ったけど、バージョンの違いによるエラー...
Next.jsとFastAPIでのprismaのバージョンの統一、prismaの設定、その他にも試行錯誤してできたけどprismaの設定が二重化してしまい管理が大変。
そこで上司から「Prismaの共通化してみたら?」とアドバイスが!
具体的に聞いてみると、Prismaを共通化することで、開発効率・保守性・信頼性を大幅に向上させることができ、Next.jsとFastAPIでバラバラに管理されていたPrisma関連ファイルを、プロジェクトのルートディレクトリに一元管理できるらしい。
管理も楽になって手間が減りそう!
ということで 「Prismaの共通化」 について今回試した手順を共有します。
今回の開発環境
- コンテナ化: Docker
- API: Python、FastAPI
- Webアプリ: TypeScript、Next.js
- データベース: MySQL
- ORM: Prisma
従来の問題点
最初は以下のようなディレクトリ構成で開発しました。
api/
├── prisma/
│ ├── schema.prisma
│ └── migrations/
└── ...
application/
├── prisma/
│ ├── schema.prisma
│ └── migrations/
└── ...
この構成では、以下のような非効率な状況が生まれていました。
-
管理の手間
- スキーマの変更やマイグレーションの実行を、WebアプリとAPIの両方で行う必要がある。
-
同期ミス
- データベースの構造変更時に、片方のサービスで対応が漏れると、バグの原因になる可能性がある。
-
バージョン管理の複雑化
- Prisma本体や関連パッケージのバージョンが異なると、予期せぬエラーが発生することがある。
共通化の内容と具体的な変更点
Prisma関連ファイルを以下のようにプロジェクトのルートディレクトリ直下にあるprisma/に集約することで共通化します。
プロジェクトルート/
├── prisma/
│ ├── schema.prisma
│ ├── migrations/
│ └── package.json, Dockerfileなど
├── api/
└── application/
具体的な変更点を見ていきましょう。
ディレクトリ構成の変更
api/prisma/とapplication/prisma/に散らばっていたschema.prismaやマイグレーションファイルを、prisma/ディレクトリに統合します。これにより一箇所でschema.prismaやmigrationファイルを管理できるようになり、管理が楽になります。
Prismaクライアントの多重生成対応
1つのschema.prismaファイルから、複数の環境用(例:Python、Node.js)のPrismaクライアントを生成できるようにします。generatorブロックを複数定義することで、これが可能になります。
generator client_py {
provider = "prisma-client-py"
output = "/app/src/generated/prisma"
binaryTargets = ["native", "debian-openssl-3.0.x"]
}
generator client_js {
provider = "prisma-client-js"
output = "/app/node_modules/.prisma/client"
binaryTargets = ["native", "debian-openssl-3.0.x"]
}
docker-compose.ymlの修正
共通のスキーマやマイグレーションを参照するように設定します。Prisma専用のコンテナを用意することで、マイグレーションの実行やPrisma Studioの操作も一元的に行えるようになります。
prisma:
build:
context: ./prisma
ports:
- "5555:5555"
environment:
- DB_URL={データベースのURL}
volumes:
- ./prisma:/app
working_dir: /app
command: tail -f /dev/null
各サービス(WebアプリやAPI)のprismaディレクトリに統合したファイル群をマウントして、各サービス内部でPrismaクライアントをジェネレートします。
(以下のコードはポイントとなる記述に絞って記載しています。)
application:
volumes:
- ./prisma:/app/prisma
command: sh -c "npm run prisma:generate"
api:
volumes:
- ./prisma:/app/prisma
command: sh -c "poetry run prisma generate --generator=client_py"
これで、Prismaの設定が共通化でき、各サービスではジェネレートコマンドを実行するだけで、同じデータベース、同じテーブルを扱うことができました!
今回の作業を行ってみて、以下が便利だなぁと思いました。
-
一つのスキーマ・マイグレーションを管理すればOK
-
フロントエンド・バックエンドで同じDB構造を参照できる
まとめ
今回の作業でWebアプリとAPIで同じデータベースにアクセスする場合は、スキーマ定義などを一元管理することでコードの可読性、保守性、効率性を向上させることができることを知りました。
他のORMやデータベースを使う時もコード上で一元化する方法はあると思うので、他の技術も学びながら可読性や保守性など高められるエンジニアとして更に成長していこうと思います!
Discussion