🚝

Prismaとモノレポ

2023/11/03に公開

この記事は、npmのworkspace機能を使ってモノレポ且つPrismaの型・スキーマを共有する構成を作るチュートリアルです。今回Turborepoは使いません。 元々workspaceは、yarnやpnpmに存在していた機能で、npmが倣って実装した経緯があるため、以下はyarn、pnpmに置き換えても問題ないかと思います。

参考

2023年現在、Prismaのスキーマを共有する方法を検索すると、下記の記事と、この記事内のコメントにたどり着くかと思います。今回まとめるのはこのコメントで提示されている方法の具体的な実装です。
https://zenn.dev/takky94/articles/23f4c814432208#comment-a3647a2250e748

npm workspaceの使い方はこちら。
https://qiita.com/frozenbonito/items/8230d4a3cb5ea1b32802

1. 構成

下記のpackages/database/にPrismaをインストールし、全てのアプリケーションでスキーマ(PrismaClient)を共有することを目的とします。

project/
 ├ apps/
 │  ├ app1/
 │  └ ...
 └ packages/
    └ database/

app1は、frontend, backendなどと置き換えてもらって構いません。

2. プロジェクトの初期化

$ cd /path/to/project
$ npm init -y

3. Prismaのインストール

ワークスペースの作成

まず、databaseというワークスペースを用意します。下記のコマンドで、packages/database/ディレクトリと、ディレクトリ内のpackage.jsonが作成されます。

$ npm -w packages/database init -y

また、ルートのnode_modules/には、databaseワークスペースへのシンボリックリンクが自動生成されます。

必要なパッケージのインストール

databaseワークスペースにPrismaをインストールしていきましょう。ワークスペース内でnpmコマンドを使うには、基本的にnpm -w {workspace} {command}と記述します。npxも同様に使えます。

$ npm -w database i @prisma/client
$ npm -w database i prisma -D
$ npx -w database prisma init

npx prisma initコマンドで、下記ファイルの雛形が用意されます。

  • packages/database/.env
  • packages/database/prisma/schema.prisma

スキーマを記述

SQLite3のデータベースを用意してみます。適宜読み替えてください。

packages/database/.env
DATABASE_URL="file:/path/to/database.sqlite"
packages/database/prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Record {
  id      Int    @id @default(autoincrement())
  title   String
  content String
}

マイグレーションとPrismaClientの生成

$ npx -w database prisma migrate dev

マイグレーションが必要ない場合はこちら。

$ npx -w database prisma generate

テストデータの用意

Prismaには、Prisma Studioという簡易的なビジュアルエディタがあるので、簡単にテストデータの追加・削除を行うことができます。

$ npx -w database prisma studio

4. アプリケーションでPrismaを使う

TypeScriptの初期設定

TypeScriptはモノレポ全体で共通の設定で使いたいため、各ワークスペースではなくプロジェクト自体に設定します。tsconfig.jsonについては、適宜変更してください。

$ npm i -D @types/node ts-node typescript
$ npx tsc --init

ワークスペースごとにTypeScriptを設定したい場合は、これまでと同様に-wオプションを使います。

$ npm -w app1 i -D @types/node ts-node typescript
$ npx -w app1 tsc --init

ワークスペースの作成

databaseと同様にワークスペースを用意します。

$ npm init -y -w apps/app1

実際にPrismaClientを使ってみる

作成したワークスペースapps/app1に、テスト用のスクリプトを用意します。

apps/app1/index.ts
import { PrismaClient } from "@prisma/client"

main()

async function main() {
  const prisma = new PrismaClient()
  const records = await prisma.record.findMany({})
  console.log(records)
}

コンパイル

下記を実行し、apps/app1/index.jsが生成されたら成功です。

$ npx -w app1 tsc

実行

生成されたファイルを実行し、Prisma Studioで入力したテストデータが表示されたら成功です。

$ node apps/app1/index.js 
[
  { id: 1, title: 'テスト1', content: 'テスト' },
  { id: 2, title: 'テスト2', content: 'テスト' }
]

5. その他のアプリケーションを追加する

ワークスペースからのPrismaClientのインポートについては、上記で見たように、階層を特に気にせずに読み込むことができます。

import { PrismaClient } from "@prisma/client"

npm create vite

各種フレームワークをセットアップする場合、Viteなどのツールを使うかと思います。

$ npm -w apps/app2 init -y
$ npm -w apps/app2 create vite@latest
✔ Project name: … .
...

この時Project nameを指定すると、ワークスペース下にもう一つ階層ができてしまうので、.と入力しましょう。npm createコマンド時に指定することもできます。

$ npm -w apps/app2 create vite@latest .
npm initは必要?

なくても問題なさそうです。
node_modulesにシンボリックリンクが貼られるかどうかの違いがあります。

npm -w apps2 create viteじゃ駄目?

これまでのコマンドではディレクトリを省略できましたが、createではワークスペースのパスを正しく入力する必要があるみたいです。

他のセットアップツールも、使い方は基本的に同じです。

$ npm -w apps/app2 create svelte@latest

npm init以外のワークスペースの追加方法

開発中に、特定のディレクトリをワークスペースとして追加したい場合は、ルートのpackage.jsonを編集する必要があります。workspaces配列にパスを追加しましょう。

package.json
{
  ...
  "workspaces": [
    "apps/app1"
  ]
}

終わりに

ワークスペース機能を触ってみて、慣れれば全く難しいものではないことが分かりました。Prismaだけでなく独自の型やユーティリティもモノレポ間で共有できて、各アプリケーションの連携が簡単になるので、選択肢の一つとして覚えておくことをオススメします。

Discussion