1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PrismaでViewを扱う

Last updated at Posted at 2025-05-30

はじめに

Prismaを使っていると少し複雑な検索を行う時、生のSQLを使用してViewを作成することがありますが、型安全ではなかったりちょっと不便です。

ちょっといい感じの方法があったのでメモします。

参考

前提条件・環境

  • Prisma 4.16.0以上
  • PostgreSQL(本記事の例)
  • TypeScript環境

サンプルスキーマ

対象のテーブルはこんな感じ

// schema.prisma
model User {
  id      Int      @id @default(autoincrement())
  email   String   @unique
  name    String?
  profile Profile?
}

model Profile {
  id     Int    @id @default(autoincrement())
  bio    String
  avatar String?
  user   User   @relation(fields: [userId], references: [id])
  userId Int    @unique
}

実装手順

1. Preview機能の有効化

schema.prismaのgeneratorブロックにpreviewFeatures = ["views"]を追加します

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["views"]
}

2. SQLビューの作成

データベースに直接SQLを実行してViewを作成します。

CREATE VIEW user_info_view AS
SELECT 
  gen_random_uuid() AS id,
  u.id AS user_id,
  u.email,
  u.name,
  p.bio,
  p.avatar,
  CASE 
    WHEN p.id IS NOT NULL THEN true 
    ELSE false 
  END AS has_profile
FROM "User" u
LEFT JOIN "Profile" p ON u.id = p."userId"
ORDER BY u.id;

3. スキーマ定義の取得

以下のコマンドを実行して、データベースからスキーマ定義を取得します

npx prisma db pull

schema.prismaに以下のような定義が自動追加されます:

view user_info_view {
  id         String? @db.Uuid
  userId     Int?
  email      String?
  name       String?
  bio        String?
  avatar     String?
  hasProfile Boolean?
  
  @@ignore
}

4. スキーマ定義の修正

自動生成された定義をPrisma Clientで使用できるように修正します。

view user_info_view {
-  id         String? @db.Uuid
+  id         String  @id @default(uuid()) @db.Uuid
  userId     Int?
  email      String?
  name       String?
  bio        String?
  avatar     String?
  hasProfile Boolean?
}

5. マイグレーションファイルの作成

空のマイグレーションファイルを作成します

npx prisma migrate dev --create-only --name add_user_info_view

生成されたマイグレーションファイルに、Step 2で使用したSQL文を記述します。

-- CreateView
CREATE VIEW user_info_view AS
SELECT 
  gen_random_uuid() AS id,
  u.id AS user_id,
  u.email,
  u.name,
  p.bio,
  p.avatar,
  CASE 
    WHEN p.id IS NOT NULL THEN true 
    ELSE false 
  END AS has_profile
FROM "User" u
LEFT JOIN "Profile" p ON u.id = p."userId"
ORDER BY u.id;

動作確認

以下のコマンドを実行して、エラーが発生しないことを確認します。

npx prisma migrate dev
npx prisma generate

新たにマイグレーションファイルが作成されなければ、セットアップは正常に完了しています。

使用例

型安全にビューを操作できるようになります。

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// 全ユーザー情報を取得
const allUsers = await prisma.user_info_view.findMany();

// プロフィールがあるユーザーのみ取得
const usersWithProfile = await prisma.user_info_view.findMany({
  where: {
    hasProfile: true
  }
});

// 特定のユーザーを検索
const userByEmail = await prisma.user_info_view.findFirst({
  where: {
    email: 'user@example.com'
  }
});

// 型安全性の恩恵
usersWithProfile.forEach(user => {
  console.log(user.email); // TypeScriptの型推論が効く
  console.log(user.bio);   // string | null として推論される
});

まとめ

もうちょっとスマートな方法ありそうだなぁとも思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?