plan-4ging

2026/03/23

Prisma

Node.js / TypeScript 向けの次世代 ORM(Object-Relational Mapper)
スキーマファイルを中心に型安全なデータアクセスを実現する設計

下記 Prisma を構成する主要な3つのコンポーネント

  • Schema:データモデルとDB接続を定義するファイル(schema.prisma
  • Client:スキーマから自動生成される型安全なクエリビルダー
  • Migrate:スキーマの変更をDBに反映するマイグレーションツール

PostgreSQL、MySQL / MariaDBなどに対応

導入方法

以下一例

# インストール
npm install prisma --save-dev
npm install @prisma/client

# 初期化(schema.prisma を生成)
npx prisma init --datasource-provider postgresql

# 生成されるファイル
.env DATABASE_URL を記載する
prisma/
  schema.prisma スキーマ定義ファイル

schema.prisma

基本構成

// ジェネレーター設定(Prisma Client の生成設定)
generator client {
  provider = "prisma-client-js"
}

// データソース設定(DB の接続情報)
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// モデル定義(テーブルの定義)
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  role      Role     @default(USER)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([email])  // インデックス
  @@map("users")    // テーブル名を指定(デフォルトは model 名)
}

// Enum定義
enum Role {
  USER
  ADMIN
  MODERATOR
}

フィールド型一覧

Prisma 型PostgreSQLMySQL備考
Stringtextvarchar(191)
Intintegerint
BigIntbigintbigint
Floatdouble precisiondouble
Decimaldecimaldecimal
Booleanbooleantinyint(1)
DateTimetimestampdatetime
Jsonjsonbjson
Bytesbytealongblob

Prisma Client

下記のようにスキーマから生成

npx prisma generate

下記のように初期化

// PrismaClient はシングルトンで管理する(開発時の接続数爆発を防ぐ)
import { PrismaClient } from '@prisma/client'

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined
}

export const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    log: process.env.NODE_ENV === 'development'
      ? ['query', 'error', 'warn']  // 開発時はクエリログを出力
      : ['error'],
  })

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma
}

CRUD 操作

createMany,findUnique/findUniqueOrThrow,aggregate,upsertなど様々なメソッドが用意されている

トランザクション処理

$transaction API, Interactive transactions などが存在

マイグレーション運用

migrateコマンドでスキーマの変更を SQL ファイルとして管理する
変更履歴がprisma/migrations/配下にファイルとして残るため、Git で差分管理・レビュー可能

# 開発環境:スキーマ変更をマイグレーションファイルに反映する
npx prisma migrate dev --name add_user_role
# → prisma/migrations/20240315123456_add_user_role/migration.sql が生成される
# → Prisma Client が自動再生成される

# マイグレーションを適用
npx prisma migrate deploy

# マイグレーションの状態確認
npx prisma migrate status

# スキーマと DB の差分確認
npx prisma migrate diff \
  --from-schema-datamodel prisma/schema.prisma \
  --to-url $DATABASE_URL

# DB からスキーマを生成(既存 DB から Prisma を導入する場合)
npx prisma db pull

その他

Client 活用

スキーマから型を自動生成するだけでなく、モデルの型・クエリ引数の型を取り出して再利用できる
anyや手書きの型定義を避け、スキーマと型を一元管理

import { Prisma } from '@prisma/client'

// モデルの型を取得する
type User    = Prisma.UserGetPayload<{}>
type UserWithPosts = Prisma.UserGetPayload<{
  include: { posts: true }
}>

// select の結果に合わせた型を取得する
type UserSummary = Prisma.UserGetPayload<{
  select: { id: true; name: true; email: true }
}>

// where 句の型
type UserWhere = Prisma.UserWhereInput

// 関数の引数型を Prisma の型で定義する
async function findUsers(where: Prisma.UserWhereInput): Promise<User[]> {
  return prisma.user.findMany({ where })
}

Client Extensions 活用

$extendsを使うことでクエリに横断的な処理(ロギング・ソフトデリートなど)を追加できる
かつての$use(Middleware)の後継

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient().$extends({
  // スロークエリログ
  query: {
    $allModels: {
      async $allOperations({ model, operation, args, query }) {
        const start  = Date.now()
        const result = await query(args)
        const end    = Date.now()

        if (end - start > 100) {
          console.warn(
            `Slow query detected: ${model}.${operation} (${end - start}ms)`
          )
        }

        return result
      },
    },
  },
})

// ソフトデリート
const prismaWithSoftDelete = new PrismaClient().$extends({
  query: {
    post: {
      async delete({ args, query: _ }) {
        // delete を update(deletedAt のセット)に差し替える
        return prisma.post.update({
          where: args.where,
          data:  { deletedAt: new Date() },
        })
      },

      async findMany({ args, query }) {
        // deletedAt が null のレコードのみ取得する
        args.where = {
          ...args.where,
          deletedAt: null,
        }
        return query(args)
      },
    },
  },
})

複数の拡張を組み合わせる場合は$extendsをチェーンする
各拡張を関数として分離しておくと再利用・テストがしやすくなる

const prisma = new PrismaClient()
  .$extends(slowQueryExtension)
  .$extends(softDeleteExtension)

接続設定


参考