💎

モノレポにおけるback/front間のPrismaの型共有の方法

2022/05/11に公開約4,600字

詳しい方いたら教えてください。めっちゃ欲しい情報ですん。
別にモノレポでなくてもいいんですが、backend/frontendをTSで開発されてる場合Prisma入れてる気がするのですがそういう時の型共有の方法、ggってもあまり出てこない気がする。

Prisma とは

Node.jsのORMです。かなり使いやすくて気に入ってます。

https://www.prisma.io/

スターもたくさんついてますね。

https://github.com/prisma/prisma

お金もたくさん調達できてるみたいでいい感じです。

https://www.prisma.io/blog/series-b-announcement-v8t12ksi6x

Prismaの型の生成

参考: Set up Prisma

上記ページをもとにサクッとinstallすると /prisma に schema.prismaというファイルが生成されます。そのファイルに、例えばこんな感じでスキーマを定義してみます。

// ユーザー
model User {
  id            String    @id @default(cuid())
  slug          String    @unique @default(cuid())
  name          String
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
  profile       Profile?
  status        Status?
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
}

定義できたらnpm install @prisma/client && npx prisma generateを実行するだけ。

すると、 node_modules/.prisma/client/index.d.ts にこんな感じでTSのtypeを生成してくれます。

/**
 * Model User
 * 
 */
export type User = {
  id: string
  slug: string
  name: string
  email: string | null
  emailVerified: Date | null
  image: string | null
  createdAt: Date
  updatedAt: Date
}

👆 こいつを共有したいンゴねぇ

すればええやん?

そうなんですけど、自分の今の環境が下記のような構成で(みんなこんなもんだと思うけど)、

.
├── .github
├── README.md
├── backend
├── docker
├── frontend
├── node_modules
├── package-lock.json
└── package.json

backend内とfrontend内とで別々のデプロイ先に行きます。なので、ルート階層にprismaを設置するとbackendやfrontend側からprismaの型を参照できず、ビルドエラーが起こってしまう。そこでやむを得ず、backendとfrontendの両方にprismaをinstallしています。超無駄。

今現在どのように型共有を行っているか

  1. ルート階層にhuskyを置く。
  2. pre-commit段階でbackendのschema.prismaの変更を検知
  3. backendのschema.prismaをfrontendのschema.prismaにコピる

1/2. husky

Prettierとかと併用するイメージのhusky。pre-commit段階で任意のコマンドを実行することができます。細かいinstall方法などは割愛。.husky/pre-commitを下記のように設定しました。

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

cd $(dirname "$0")/../

# git addした差分の一覧を変数に格納
changedFiles=$(git diff --diff-filter=d --cached --name-only)
echo $changedFiles

# 差分の一覧にbackendのprisma内のファイルが含まれている場合はnpm run db:migrate実行
if [[ "$changedFiles" == *backend/prisma* ]]; then
cd ./backend && npm run db:migrate
echo "[~/.husky/pre-commit] スキーマ情報の更新あり。フロントのPrismaも更新"
else
echo "[~/.husky/pre-commit] スキーマ情報の更新なし"
fi

3. backendのschema.prismaをfrontendのschema.prismaにコピる

👇 backendのpackage.jsonはこんな感じ。

  "scripts": {
    "db:edited": "npx prisma generate",
    "db:reset": "npx prisma db push --accept-data-loss --schema=./prisma/reset.prisma",
    "db:create": "npx prisma db push",
    "db:migrate": "npm run db:reset && npm run db:create && npm run db:edited && cp -f ./prisma/schema.prisma ../frontend/prisma/schema.prisma && cd ../frontend && npx prisma generate",

db:migrateの内訳としては、

  1. まずreset.prismaというファイルを用意し、db:resetで既存のモデルを全て破壊
  2. その後、db:createでschema.prismaをDBに反映させる
  3. 次にnpx prisma generate実行でschema.prismaに合わせて型生成を行う
  4. cpコマンドでbackendのschema.prismaをfrontend内にコピる
  5. frontでもnpx prisma generateを実行

以上の手順で動いてはいる

動きはするし、やりたいことは満たせてるんですが、backendとfrontendで2つPrismaを入れなきゃいけない & front側では型としてしか使ってないので「う〜ん」と。front側を@next/bundle-analyzerでバンドルサイズ測っても、Prismaがかなり大きな部分占めてるんですよね。

backend/node_modules/.prisma/client/index.d.tsをコピるってのも試そうとしたんですが、なんかダメだった気がする。確か同階層の他のモジュールimportしまくってるから結局ディレクトリごとコピらないと意味ない的なだったような。

追記: outputというオプションもある

https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/generating-prisma-client#using-a-custom-output-path
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider        = "prisma-client-js"
+ output          = "../../types"
}

以上のようにschema.prismaにoutputパスを書いてやればルートに置くことができます。(別にbackend内でもいいですがそれだとbackend/node_modules/.prisma/clientに設置するデフォのやり方と遜色ない)

 .
 ├── README.md
 ├── backend
 ├── docker
 ├── frontend
 ├── node_modules
 ├── package-lock.json
 ├── package.json
+└── types

ただし、これはPrismaデフォルトの使い方から外れることになります。

https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/generating-prisma-client#why-is-prisma-client-generated-into-node_modulesprismaclient-by-default

また、outputも配列で受け付けてくれるわけではなく、generate時にいっぺんにbackendとfrontendに配置できるわけではないため、ルートに配置したtypesディレクトリをbackend、frontendに複製するシェルを書く必要などもありそう。

というわけで色々足掻いてるので何か良い方法あったら教えてくださいな。

Discussion

ログインするとコメントできます