💭

PrismaのモデルからDB上のテーブル名を取り出す方法

2024/10/05に公開

後述の アプローチ2 が良かろう。

シチュエーション

型がしっかりしているPrismaといえど、すべてをPrismaモデルだけで扱えない仕事もあるでしょう。

  • テーブルを扱うなんかメタなスクリプトだとか。
  • 動的なテーブルを扱う複雑なSQL(DML,DDL)をtypescriptコードから呼び出す時とか。

そんなときに prisma.xxx または 型安全な Prisma のモデル文字列 からRDB上のtable名文字列を得たいことがあるでしょう。

アプローチ1: 文字列をゴニョゴニョする系

model/tableの命名規則として、こうやっているプロジェクトが多いのではなかろうか

  • model名: UpperCamelなSingular.

    例: model UserEvent { .... }

  • prisma client オブジェクトに生えているプロパティ名は lowerCamelSingular

    例: prisma.userEvent.findMany(...)

  • RDBのtable名は snake_case な plural

    例: create table user_events

How to do
↑上記のパターンの場合はこうかける

import { Prisma } from "@prisma/client";
import pluralize from "pluralize"
import { snakeCase } from "lodash";

function toTableName(prismaModelName: Prisma.ModelName) {
    return pluralize(snakeCase(prismaModelName));
}

// How to use
toTableName(prisma.userEvent) // -> "user_events"

問題

  • plural化のところで不可算名詞やperson → people などの不規則変化に関するトラブルがあるかも
  • DBのテーブル名 と Prisma Schema のモデル名 が機械的な文字列操作で変換できないケースではこの方式は採用できない
    • 例1: model "Post" ↔ table "articles"
    • 例2: アプリケーション内の各model/tableごとに名称パターンがカオスな例
      model "User" は table "users"
      (UpperCamelSingular → snake_case_plural)
      なのに
      model "UserEvents" は table "user_event"
      (UpperCamelPlural → snake_cse_singular)

アプローチ2. 実際のDBをとりだす方法

  • これならDBのテーブル名 が Prisma Schema のモデル名と機械的な文字列操作では取り出せないケースでも確実にいける

prisma extension で取り出すコードの例があった。
https://github.com/prisma/prisma/issues/11940#issuecomment-1722904361

vitestを使っていると、vPrismaの初期化の都合で$extends(prismaExtension) する方法がみつからなかった。
そこで prisma extension ではない形にしてみた

import { Prisma } from "@prisma/client";
import { prisma } from "<your project's instantiated prisma client>";

type PrismaModel = (typeof prisma)[Uncapitalize<Prisma.ModelName>];

function getTableName(model: PrismaModel):string {
    const modelName = Prisma.getExtensionContext(model).$name;
    if (!modelName) throw new Error("Model name not found");
    return (prisma as any)._runtimeDataModel.models[modelName].dbName;
}

// How to use
getTableName(prisma.user) // -> "users"

その他めも

このコードの type PrismaModel は prisma.xxx を受け取れる型。

import { Prisma } from "@prisma/client";

export type PrismaModel = (typeof prisma)[Uncapitalize<Prisma.ModelName>];

/* === type test cases === */
import { prisma } from "<your project's instantiated prisma client>";
const m1: PrismaModel = prisma.user;
const m2: PrismaModel = prisma.$transaction; // これはコンパイルエラー

Discussion