🎃

Laravel から Nuxtに移行してprismaに嵌った話

に公開

TL;DR

「Laravel Eloquent の甘い蜜に慣れていた自分が、Nuxt + Prisma の世界に飛び込み、Timezone や deletedAt で苦しんだ話。そして Eloquent 風の ORM ヘルパーを OSS として作った記録。」

はじめに

私は長年 Laravel(PHP)でアプリケーションを開発してきました。
特に Eloquent ORM のシンプルさと、直感的にチェーンできるクエリビルダーに魅了されていました。

しかし、新しい現場へ入り Nuxt と Prisma(TypeScript ORM)に移行したことで、思わぬ壁に次々と直面することになったのです。

Prisma で最初に感じた「カルチャーショック」

  1. Timezone の罠
    Laravel では Carbon(DateTime) が自動でアプリのタイムゾーンを適用してくれるため、ユーザーの表示用の時間は常に JST(Asia/Tokyo)で扱えました。

しかし Prisma では:

DB(UTC) ↔ Prisma(UTC) ↔ フロントエンド(UTC)

アプリケーション側で意識的にタイムゾーン変換をしないとすべて UTC。
これにより、画面表示がすべて9時間ズレたタイムスタンプになってしまいました。

→ 結局 dayjs や date-fns-tz を導入し、手動で変換する羽目に。

  1. deletedAt(ソフトデリート)の扱い
    Laravel Eloquent の場合:
User::where('active', true)->get(); // 自動的に deleted_at 除外

Prisma では除外されない。

await prisma.user.findMany({ where: { active: true } }); // 削除済みも出てくる

そのため、毎クエリに:

where: { deletedAt: null }

を明示的に書かなければならない。

→ ソフトデリートの文化が違う

  1. クエリビルダーの不自由さ
    Eloquent のように:
User::where('active', true)->with('profile')
  ->orderBy('created_at', 'desc')->get();

これが Prisma では型の壁と構文の制限で毎回書き直し。
チェーンビルダーは存在しないので、ついに独自でラッパー(ヘルパー)を作り始めた。

OSS「Prisma Relation Helper Generator」を開発

Prisma の制約を乗り越えるため、以下のような Laravel Eloquent 風のクエリビルダーを自動生成する OSS を作成。

注記:下記は未実装ですが、次回バージョンにて展開予定

const user = await UserHelper
    .where({ name: "Taro" })
    .with('profile')
    .orderBy('id', 'desc')
    .first();

自動生成なので、Prisma スキーマを書き換えればヘルパーも再生成可能。

GitHub
https://github.com/sakumoto-shota/prisma-relation-helper-generator

目標

  • Prisma に Eloquent チェーンの書き心地を導入
  • Nuxt + Prisma でも Laravel ライクな開発体験を実現

結論

  • Prisma は型安全・速度・フロントエンド親和性は素晴らしいが 学習コストと文化の違いが大きい。
  • Laravel の甘やかしに慣れていると最初は絶望する。
  • しかし自分でラッパーを書く自由度があるのも Prisma の魅力。
  • 今後も Laravel の良いところを Prisma 世界に持ち込んでいきたい。

Discussion