EY-Office ブログ

PGliteをPrismaで使ってみたが時期尚早だったか?

前々回書いたPGliteの続きです。PrismaのドキュメントDatabase driversによるとPGliteはCommunity-maintained database driver adaptersとしてアダプターが用意されているようなので試してみました。

PGlitePrisma

RailsプログラマーのためのPrisma入門のコードを動かしてみよう

以前書いたRailsプログラマーのためのPrisma入門(1)(2)(3)で使ったコードをPGliteで動かしてみました。

まず最初に、Prismaはブラウザー上では動かないようなので、PGliteもNode.js上で動かします。

pglite-adapter-prismaに書かれている手順で RailsプログラマーのためのPrisma入門(1) のコードをPGliteで動かしてみました。結論から言うと動きました

ハマった事 (1)

しかし、簡単ではありませんでした。まずpglite-adapter-prisma

The adapter only supports Prisma Client. Prisma migration and introspection are not supported, though I want to make a cli tool to help with that in the future.

と書かれています。DeepLで翻訳すると

このアダプターはPrismaクライアントのみをサポートしています。Prismaマイグレーションとイントロスペクションはサポートしていません。 将来的にはそれを手助けするcliツールを作りたいと思っている。

という事で、マイグレーションは諦めCREATE TABLEを行うプログラムを書きました。

import { PGlite } from '@electric-sql/pglite';
const db = new PGlite('./pg_data');

(async () => {
  await db.exec(`
    CREATE TABLE "cats" (
      "id" SERIAL NOT NULL,
      "name" VARCHAR(255) NOT NULL,
      "reference_image_id" VARCHAR(255),
      "temperament" VARCHAR(255),
      "description" TEXT,
      "country_id" INTEGER,
      CONSTRAINT "cats_pkey" PRIMARY KEY ("id"));

    CREATE TABLE "countries" (
      "id" SERIAL NOT NULL,
      "name" VARCHAR(255) NOT NULL,
      "country_code" VARCHAR(2) NOT NULL,
      CONSTRAINT "countries_pkey" PRIMARY KEY ("id"));

    ALTER TABLE "cats" ADD CONSTRAINT "cats_country_id_fkey" FOREIGN KEY ("country_id")
      REFERENCES "countries"("id") ON DELETE SET NULL ON UPDATE CASCADE;
  `);
})();

ハマった事 (2)

これはPrismaを使っていれば常識なのかもしれませんが、schema.prismagenerator clientを変更したので npx prisma generate を実行して@prisma/clientを再生成しないといけません。
最初これを忘れていて、コードがエラーになっていました。

schema.prisma ファイル
enerator client {
  provider = "prisma-client-js"
  previewFeatures = ["driverAdapters", "relationJoins"]
}

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

model Cat {
  id                 Int      @id @default(autoincrement())
  name               String   @db.VarChar(255)
  countryId          Int?     @map("country_id")
  referenceImageId   String?  @db.VarChar(255) @map("reference_image_id")
  temperament        String?  @db.VarChar(255)
  description        String?  @db.Text
  country            Country? @relation(fields: [countryId], references: [id])

  @@map("cats")
}

model Country {
  id                 Int      @id @default(autoincrement())
  name               String   @db.VarChar(255)
  countryCode        String   @db.VarChar(2) @map("country_code")
  cats               Cat[]

  @@map("countries")
}

変更点 (3)

pglite-adapter-prismaにあるようにPrismaClientの初期化のコードを変えないといけません。

import { PrismaClient } from '@prisma/client';
import { CountriesData } from './seeds/countries';
import { CatsData } from './seeds/cats';

const prisma = new PrismaClient();

↓ 変更

import { PGlite } from '@electric-sql/pglite';
import { PrismaPGlite } from 'pglite-prisma-adapter';
import { PrismaClient } from '@prisma/client';
import dotenv from 'dotenv';

import { CountriesData } from './seeds/countries';
import { CatsData } from './seeds/cats';

dotenv.config();
const connectionString = `${process.env.DATABASE_URL}`;
const client = new PGlite(connectionString);
const adapter = new PrismaPGlite(client);
const prisma = new PrismaClient({ adapter });

やはりpsqlコマンドが欲しい〜

前回はブラウザーで動かしているのでPGlite REPL React Componentが使えましたが、今回はnode.js上で動かしているので使えません。

結局、超手抜きのpsql風のプログラムを書いてしまいました。

import { PGlite, Results } from '@electric-sql/pglite';

const db = new PGlite('./pg_data');

const lastArg = process.argv[process.argv.length - 1];
const beforeLastArg = process.argv[process.argv.length - 2];

(async () => {
  if (lastArg == 'd') {
    const sql = "SELECT table_name FROM information_schema.tables WHERE table_type
      = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')";
    const result:Results = await db.query(sql);
    console.log(result.rows);
  } else if (beforeLastArg == 'd') {
    const sql = `SELECT column_name, data_type, character_maximum_length, column_default,
      is_nullable FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${lastArg}'`;
    const result:Results = await db.query(sql);
    console.log(result.rows);
  } else   {
    const result = await db.query(lastArg);
    console.log('rows:',result.rows,);
    console.log('affectedRows:', result.affectedRows);
  }
})();

コマンドラインで指定したSQL文が実行できます、まだdでテーブルの一覧、d テーブル名でテーブル定義が表示されるようにしまいした。結果のフォーマッティングやエラー処理はありません。😅

まとめ

という事で現状ではPGlite + Prismaは実用的ではないと思います。 ただしPrisma(ORM)を使わなければ実用的だと思います。

マイグレーションが使えるようになり、psql的なツールが出たら使ってみたいです。

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ