モノレポ環境でPrismaを利用する
Prismaを利用することが多いがこれまではPrismaについてはこれ1つでProjectを作成していた。
でDB Migrationをするという役割を与えていた。
理由として一番大きな理由はコミット履歴。
何をいつ誰がという情報がコミット履歴に集約されるためDB更新履歴としていたから。
モノレポについてはまだ賛否両論あるがモノレポの環境化でのPrismaの扱いについて検討したくなった。
こんな構成を考えてみる。
APIエリア、JOBエリアがあり、それぞれがDBへの接続をPrismaを利用する。
Prismaの定義が記述されているschema.prismaファイルを両方に配置してgenerateするのは効率が悪いのでモノレポ方式でうまく運用できないかを考えたい。
方法としてはこれなんだろうなと思う。
1つ目 prisma clientモジュールをAPIエリアのProjectで参照できるようにパス解決する方法
このあたりで一回 chatgpt 4oに要件を整理してもらう。
以下、chatgpt 4oの回答
以下の手順で「multi-project-prisma」をモノレポとして構築することを提案します。
構成概要
-
プロジェクトA:
- S3に格納されたデータをLambdaを経由して取得。
- Prismaを用いてMySQLにデータを登録。
- 関連リソース: S3、Lambda、Prisma、MySQL。
-
プロジェクトB:
- API GatewayからのリクエストをLambda経由で受け取り、Prismaを使用してMySQLからデータを取得。
- 関連リソース: API Gateway、Lambda、Prisma、MySQL。
-
プロジェクトC:
- Prismaを用いてDBスキーマを定義し、AとBで共通使用する。
- 関連リソース: Prisma。
モノレポ構成の提案
1. ディレクトリ構造
multi-project-prisma/
│
├── packages/
│ ├── project-a/ # S3 → Lambda → MySQL へのデータ登録機能
│ │ ├── src/
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── ...
│ ├── project-b/ # API Gateway → Lambda → MySQL へのデータ取得機能
│ │ ├── src/
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── ...
│ └── project-c/ # Prismaのスキーマ定義
│ ├── prisma/
│ │ ├── schema.prisma
│ ├── package.json
│ ├── tsconfig.json
│ └── ...
│
├── node_modules/
├── package.json
└── tsconfig.json
-
packages/project-a
とpackages/project-b
は、それぞれLambda関数を実装。 -
packages/project-c
は、Prismaスキーマ定義を管理。
2. パッケージ管理と共有
multi-project-prisma
のルートにpackage.json
を配置し、モノレポとしてYarnやnpmのワークスペース機能を利用します。
package.json
の設定例:
{
"name": "multi-project-prisma",
"private": true,
"workspaces": [
"packages/project-a",
"packages/project-b",
"packages/project-c"
]
}
3. 共通リソースの管理
プロジェクトCで定義したPrismaスキーマをAとBで利用します。project-c
内に定義されたPrismaのスキーマは、ワークスペースを介して他のプロジェクトから参照されます。
-
project-a
とproject-b
では、project-c
のPrismaスキーマをインポートして使用します。
4. デプロイ戦略
-
project-a
とproject-b
は、それぞれ独立してデプロイ可能にするため、Serverless Frameworkを使用します。 - Prismaは、共通のDB定義として、マイグレーション時に
project-c
からコマンドを実行します。
次のステップ
-
パッケージのセットアップ: 各プロジェクト内で
yarn
またはnpm
を使って依存関係をインストール。 -
Prismaスキーマ定義:
project-c
でスキーマを作成し、各プロジェクトで利用できるように設定。 -
Lambdaの実装:
project-a
とproject-b
で、それぞれのビジネスロジックをLambdaに実装。 - デプロイの設定: Serverless FrameworkやAWS CDKを使用して、AWS LambdaとAPI Gatewayの設定を行う。
この構成により、効率的なモノレポ管理が可能になり、リソースや処理の重複を最小限に抑えることができます。
だいぶ優秀なアウトプットをもらったので、さらにchatgpt o1-previewに上記の結果をそのままインプットに利用してさらに具体的な構築手順を検討してもらう。
以下、結果
具体的なプロジェクト構築方法
あなたの提案を基に、具体的なプロジェクト構築手順を以下にまとめました。各プロジェクトのセットアップから、Prismaスキーマの共有、デプロイ方法まで詳しく説明します。
目次
- モノレポのセットアップ
- Project-C(Prismaスキーマ定義)の構築
- Project-AとProject-Bの構築
- Prismaスキーマの共有方法
- デプロイの設定
- 環境変数の管理
- 注意点
- まとめ
モノレポのセットアップ
1. ルートディレクトリの作成と初期化
mkdir multi-project-prisma
cd multi-project-prisma
npm init -y
2. パッケージマネージャーの選択
- Yarnの場合: Yarn Workspacesを利用します。
- npmの場合: npm v7以降のワークスペース機能を利用します。
この例では、npmを使用します。
package.json
の設定
3. ルートのpackage.json
にワークスペースの設定を追加します。
{
"name": "multi-project-prisma",
"private": true,
"workspaces": [
"packages/*"
]
}
Project-Cの構築
1. ディレクトリの作成と初期化
mkdir -p packages/project-c
cd packages/project-c
npm init -y
2. Prismaのインストールと初期化
npm install prisma @prisma/client
npx prisma init
schema.prisma
の設定
3. prisma/schema.prisma
を編集して、データモデルを定義します。
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
output = "../generated/project-c-client"
package = "project-c-client"
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}
4. マイグレーションの実行
npx prisma migrate dev --name init
5. パッケージのエクスポート設定
package.json
に以下を追加します。
{
"name": "project-c",
"version": "1.0.0",
"main": "index.js",
"types": "index.d.ts",
"files": [
"generated/"
],
"dependencies": {
"@prisma/client": "^4.0.0"
},
"devDependencies": {
"prisma": "^4.0.0"
},
"scripts": {
"build": "prisma generate"
}
}
Project-AとProject-Bの構築
1. ディレクトリの作成と初期化
mkdir -p packages/project-a
cd packages/project-a
npm init -y
同様にpackages/project-b
も作成・初期化します。
2. 必要なパッケージのインストール
npm install @prisma/client aws-sdk
npm install --save-dev typescript ts-node
3. TypeScriptの設定
tsconfig.json
を作成します。
{
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true
},
"include": ["src"]
}
project-c
の依存関係追加
4. package.json
にproject-c
を追加します。
{
"dependencies": {
"project-c": "1.0.0",
"@prisma/client": "^4.0.0",
"aws-sdk": "^2.1000.0"
}
}
5. ビジネスロジックの実装
packages/project-a/src/index.ts
)
Project-A (import { PrismaClient } from 'project-c/generated/project-c-client';
import { S3 } from 'aws-sdk';
const prisma = new PrismaClient();
const s3 = new S3();
export const handler = async (event: any) => {
// S3からデータ取得
const data = await s3.getObject({
Bucket: 'your-bucket',
Key: 'your-key'
}).promise();
// データをパースしてデータベースに登録
await prisma.user.create({
data: {
name: 'Example Name',
email: 'example@example.com'
}
});
return {
statusCode: 200,
body: JSON.stringify({ message: 'Data inserted successfully' })
};
};
packages/project-b/src/index.ts
)
Project-B (import { PrismaClient } from 'project-c/generated/project-c-client';
const prisma = new PrismaClient();
export const handler = async (event: any) => {
// データベースからデータ取得
const users = await prisma.user.findMany();
return {
statusCode: 200,
body: JSON.stringify(users)
};
};
Prismaスキーマの共有方法
1. パッケージとしての共有
project-c
で生成したPrisma Clientをproject-a
とproject-b
から利用します。
-
project-c
でPrisma Clientをビルドし、generated
ディレクトリに出力。 - 各プロジェクトで
project-c
を依存関係としてインストール。
2. 参照の方法
project-a
とproject-b
で、Prisma Clientを以下のようにインポートします。
import { PrismaClient } from 'project-c/generated/project-c-client';
デプロイの設定
1. Serverless Frameworkのインストール
各プロジェクトでServerless Frameworkと関連パッケージをインストールします。
npm install --save-dev serverless serverless-webpack webpack webpack-node-externals typescript ts-loader
serverless.yml
の設定
2.
packages/project-a/serverless.yml
)
Project-A (service: project-a
provider:
name: aws
runtime: nodejs14.x
region: ap-northeast-1
functions:
handler:
handler: dist/index.handler
events:
- s3:
bucket: your-bucket
event: s3:ObjectCreated:*
plugins:
- serverless-webpack
custom:
webpack:
webpackConfig: './webpack.config.js'
includeModules: true
packages/project-b/serverless.yml
)
Project-B (service: project-b
provider:
name: aws
runtime: nodejs14.x
region: ap-northeast-1
functions:
handler:
handler: dist/index.handler
events:
- http:
path: /users
method: get
plugins:
- serverless-webpack
custom:
webpack:
webpackConfig: './webpack.config.js'
includeModules: true
webpack.config.js
の設定
3. 各プロジェクトでwebpack.config.js
を作成します。
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');
module.exports = {
entry: slsw.lib.entries,
target: 'node',
externals: [nodeExternals()],
mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
resolve: {
extensions: ['.js', '.ts']
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader'
}
]
}
};
4. デプロイの実行
各プロジェクトで以下を実行します。
npx serverless deploy
環境変数の管理
.env
ファイルの使用
1. 各プロジェクトのルートに.env
ファイルを作成し、データベース接続情報などを記載します。
DATABASE_URL=mysql://user:password@host:port/database
serverless.yml
での環境変数設定
2. provider:
environment:
DATABASE_URL: ${env:DATABASE_URL}
dotenv
のインストール
3. npm install dotenv
4. コード内での環境変数の読み込み
import { config } from 'dotenv';
config();
注意点
-
Prisma Clientの再生成: スキーマが変更された場合、
project-c
でprisma generate
を再実行する必要があります。 -
バージョン管理: すべてのプロジェクトで
@prisma/client
とprisma
のバージョンを一致させます。 - 依存関係のループに注意: モノレポ内で依存関係のループが発生しないようにします。
まとめ
以上が、あなたの提案に基づいた具体的なプロジェクト構築方法です。モノレポを活用し、Prismaスキーマを共通化することで、開発効率と保守性を高めることができます。
- モノレポの利点: 依存関係の管理が容易になり、コードの再利用性が向上します。
- Prismaの活用: 共通のデータモデルを使用することで、データ整合性を保ちます。
- Serverless Frameworkの利用: 簡単にLambda関数をデプロイでき、インフラストラクチャの管理が楽になります。
何か不明点や追加の質問があれば、お気軽にお知らせください。
かなり具体的な手順を生成することができた。
webpackとかオワコンのモジュールを提案しているのでそこは調整が必要。
schema.prismaを再実行するタイミングについて自動化含めて chatgpt o1-previewに検討してもらう。
ほぼ、完璧な回答じゃないかなと思う。
Prisma Clientの再生成を自動化することで、スキーマ変更時に手動でprisma generate
を実行する必要を無くし、開発フローを効率化することが可能です。これを実現するために、いくつかの方法を提案します。
再生成の自動化方法
1. Git Hooksを利用する
Husky
を使って、スキーマ変更時に自動的にprisma generate
を実行するGitフックを設定します。
手順:
Husky
とlint-staged
のインストール
npm install husky lint-staged --save-dev
- Huskyのセットアップ
npx husky install
package.json
にHuskyの設定を追加
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"packages/project-c/prisma/schema.prisma": [
"npx prisma generate --schema=packages/project-c/prisma/schema.prisma"
]
}
}
この設定では、schema.prisma
が変更された場合に自動でprisma generate
が実行されます。
2. CI/CDでの自動実行
CI/CDパイプラインにprisma generate
を組み込むことで、デプロイ時に自動で最新のPrisma Clientが生成されるようにします。
手順:
-
GitHub Actionsの設定
prisma generate
を自動で実行するGitHub Actionsのワークフローを設定します。
-
.github/workflows/ci.yml
に以下の設定を追加:
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
prisma_generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- run: npm install
- run: npx prisma generate --schema=packages/project-c/prisma/schema.prisma
- run: npm test
この設定により、コードがmain
ブランチにプッシュされたときやプルリクエスト時に、Prisma Clientが自動生成されます。
postinstall
スクリプトで自動化
3. package.json
のpostinstall
スクリプトを利用して、依存関係のインストール後に自動的にPrisma Clientを生成する方法です。
手順:
package.json
にpostinstall
スクリプトを追加
-
packages/project-c/package.json
に以下を追加:
{
"scripts": {
"postinstall": "prisma generate --schema=prisma/schema.prisma"
}
}
これにより、npm install
やyarn install
が実行された際に、自動的にPrisma Clientが生成されます。
4. 開発時のウォッチモード
Prismaはprisma generate
を自動実行するウォッチモードも提供しています。これにより、スキーマが変更されるたびに自動でPrisma Clientが再生成されます。
手順:
- ウォッチモードの設定
npx prisma generate --watch
これにより、開発中にスキーマが変更されるたびにPrisma Clientが再生成されるようになります。
5. Makefileを利用する
Makefileを利用して、ビルドプロセスにPrisma Clientの再生成を含めることができます。make
コマンドで、ビルドやテストなどと一緒にprisma generate
を実行できます。
手順:
- Makefileの作成
- プロジェクトルートに
Makefile
を作成:
build:
npx prisma generate --schema=packages/project-c/prisma/schema.prisma
npm run build
- 実行方法
make build
この方法では、ビルドの際にPrisma Clientも自動で生成されます。
結論
Prisma Clientの再生成を自動化するためには、開発フローに合った方法を選ぶことが重要です。
- Git Hooks: スキーマ変更のコミット時に自動で再生成。
- CI/CD: デプロイ時に必ず再生成を行う。
- postinstallスクリプト: 依存関係のインストール後に再生成。
- ウォッチモード: 開発中のスキーマ変更を即座に反映。
- Makefile: ビルドプロセスにPrismaの再生成を統合。
これらの方法を組み合わせることで、スキーマ変更の際に手動で再生成を行う必要をなくし、開発効率が向上します。
どの方法を採用するかは、プロジェクトの規模やワークフローによって選択するのが良いでしょう。