Open9

RailsでマイグレーションされたDBスキーマをPrismaのschemaに取り込む

bisquebisque

RailsでマイグレーションされたDBを、Node.jsからPrismaを使ってアクセスできるようにPrismaのschemaを整えます。

既存のDBはPostgreSQLです。

bisquebisque

まずは prisma db pull する

既存のDBを取り込む手順がドキュメントにありますので、それに従いschemaを作成します。

https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases-typescript-postgres

  1. prismaのcliを追加
yarn add -D prisma
  1. prismaの初期化
npx prisma init
  1. .env にDBの接続設定を設定。
  2. prisma db pull を実行して既存のスキーマをschema.prismaにインポートする
npx prisma db pull

./prisma/schema.prisma に既存のDBのスキーマが書き込まれました。

このあと npx prisma generate で型定義も生成できます。

bisquebisque

ネーミングルールを合わせる

Node.js(TypeScript)のコードに snake_case が紛れ込むと、のちのち混乱のもとになりますので、schemaファイルで名前を合わせておきます。

modelの名前のmapping

以下は prisma db pull を実行した直後の状態です。

model books {
  id                          BigInt    @id @default(autoincrement())
}

booksBook にするのが推奨される名前です。

model Book {
  @@map("books")
  id                          BigInt    @id @default(autoincrement())
}

modelのfieldの名前のmapping

fieldの名前も同様にマッピングします。

  created_at                  DateTime  @db.Timestamp(6)
  updated_at                  DateTime  @db.Timestamp(6)
  createdAt                  DateTime  @db.Timestamp(6)  @map("created_at")
  updatedAt                  DateTime  @db.Timestamp(6)  @map("updated_at")
bisquebisque

bigintをJSON.strinfigyできない

Railsのデフォルトのidフィールドはbigint型として定義されますが、このままstringifyしようとすると Do not know how to serialize a BigInt となります。

https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields#serializing-bigint

ワークアラウンドとして JSON.stringify をカスタマイズする方法が載っています。

また、 fast-json-stringify を使うのもいいかもしれません。こちらはtoStringしてからintegerに変換するようです。(未確認)

fastify ではシリアライザにfast-json-stringifyが使われているので、レスポンスのschema定義をすれば数値として出力されました。

  fastify.route({
    method: "GET",
    url: "/",
    handler: async (req, res) => {
      const book = await fastify.prisma.book.findFirst();
      if (!book) {
        return res.code(404);
      }
      return book;
    },
    schema: {
      response: {
        200: {
          id: { type: "number" },
        },
      },
    },
  });

=> {"id":9}

bisquebisque

Railsがデフォルトをbigintにしているのは、後からintをbigintに変えることが大変だからなんだけど、intを超える可能性が限りなくゼロに近いのであれば、prismaの型定義はInt型と定義するでも良いかもしれません。

model Book {
  @@map("books")
  id                          Int    @id @default(autoincrement()) // 本当はBigInt
}
bisquebisque

リレーションシップ

https://www.prisma.io/docs/concepts/components/prisma-schema/relations

ActiveRecordで設定していた belongs_tohas_many などのアソシエーションは、prismaではスキーマ定義で設定します。

belongs_to :user の場合

model Book {
  userId                      Int       @map("user_id")
  user                        User      @relation(fields: [userId], references: [id])

has_many :books の場合

model User {
  books                    Book[]

これで、以下のように型やメソッドが追加されます。

      const book = await fastify.prisma.book.findFirst({
        include: { user: true },
      });
      book!.user;

      const user = await fastify.prisma.user.findFirst({
        include: { books: true },
      });

      user!.books;

include を含まない場合、結果の型にuserやbooksが含まれないのが素晴らしいですね。

bisquebisque

ポリモーフィック

ActiveRecordのアソシエーションで重宝してよく使われるのが polymorphic ではないでしょうか。prismaでは、現時点ではActiveRecordと同様の機能が実装される予定はないようです。アプリケーションのロジックで実装を行う必要があります。

https://github.com/prisma/prisma/issues/1644