高度な機能
Raw SQL、Client Extensions、Connection Pooling、Read Replica、 soft delete、ロギング、Edge / Serverless 対応まで。
$queryRaw / $executeRaw
Prisma の API で表現できない SQL を直接実行する。
テンプレートリテラル形式(推奨・自動エスケープ)
type Result = { id: string, title: string, rank: number }
const rows = await prisma.$queryRaw<Result[]>`
SELECT id, title,
ts_rank(to_tsvector('simple', title), plainto_tsquery('simple', ${query})) AS rank
FROM "Post"
WHERE to_tsvector('simple', title) @@ plainto_tsquery('simple', ${query})
ORDER BY rank DESC
LIMIT 20
`
動的なクエリ
// $queryRawUnsafe(SQL Injection 注意)
const tableName = "Post" // ここをユーザ入力にしない!
await prisma.$queryRawUnsafe(
`SELECT * FROM "${tableName}" WHERE id = $1`,
id
)
// 安全な動的構築
const where = Prisma.sql`status = ${status}`
const order = published ? Prisma.sql`ORDER BY views DESC` : Prisma.empty
await prisma.$queryRaw<Post[]>`
SELECT * FROM "Post" WHERE ${where} ${order}
`
Prisma.sql ヘルパ
- Prisma.sql`...`: 安全な SQL 断片
- Prisma.empty: 空 SQL
- Prisma.join([...], ", "): 複数値の結合
- Prisma.raw("..."): エスケープなし(識別子用)
$extends(Client Extensions)
Prisma Client を拡張する公式機構。$use(middleware)の後継で、
現代ではこちらが推奨。
カスタムメソッド
const xprisma = prisma.$extends({
model: {
user: {
async signUp(email: string) {
return xprisma.user.create({
data: { email, profile: { create: {} } }
})
},
async findActive() {
return xprisma.user.findMany({ where: { lastLogin: { gte: new Date(Date.now() - 30 * 86400_000) } } })
},
}
}
})
await xprisma.user.signUp("alice@example.com")
await xprisma.user.findActive()
計算フィールド(result)
const xprisma = prisma.$extends({
result: {
user: {
fullName: {
needs: { firstName: true, lastName: true },
compute(user) {
return `${user.firstName} ${user.lastName}`
}
}
}
}
})
const u = await xprisma.user.findUnique({ where: { id } })
u?.fullName // 自動的に存在する
クエリのフック(query)
const xprisma = prisma.$extends({
query: {
$allModels: {
async $allOperations({ model, operation, args, query }) {
const start = performance.now()
const result = await query(args)
const ms = performance.now() - start
console.log(`${model}.${operation} took ${ms.toFixed(1)}ms`)
return result
}
}
}
})
Soft Delete を実装
const xprisma = prisma.$extends({
query: {
post: {
async findMany({ args, query }) {
args.where = { ...args.where, deletedAt: null }
return query(args)
},
async delete({ args, query }) {
// 物理削除を論理削除に置換
return prisma.post.update({
where: args.where,
data: { deletedAt: new Date() },
}) as any
}
}
}
})
Connection Pool
埋め込み Pool(既定)
各 PrismaClient が内部に Pool を持つ。connection_limit パラメータで上限を制御:
DATABASE_URL="postgresql://...?connection_limit=10&pool_timeout=10"
外部 Pool(Serverless 必須)
- PgBouncer: Postgres の代表的な Pool
- Supabase Supavisor
- Neon Serverless Driver
- Prisma Accelerate: 公式の Pool + キャッシュ層
PgBouncer 経由(pgbouncer=true)
DATABASE_URL="postgresql://...:6543/db?pgbouncer=true"
DIRECT_URL="postgresql://...:5432/db" # migrate 用
Prisma Accelerate
公式 SaaS(HTTP ベースのコネクションプーラー + クエリキャッシュ)。 Vercel / Cloudflare Workers / Edge で長距離レイテンシを大幅短縮。
npm install @prisma/extension-accelerate
import { PrismaClient } from "@prisma/client"
import { withAccelerate } from "@prisma/extension-accelerate"
const prisma = new PrismaClient().$extends(withAccelerate())
// クエリキャッシュ
const popular = await prisma.post.findMany({
where: { published: true },
cacheStrategy: { ttl: 60, swr: 300 },
})
Driver Adapters(Edge 対応)
Cloudflare Workers / Deno / Bun などのNode API が無い環境で プロバイダ固有の HTTP ドライバを使う仕組み。
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
// Neon
import { neon } from "@neondatabase/serverless"
import { PrismaNeonHTTP } from "@prisma/adapter-neon"
import { PrismaClient } from "@prisma/client"
const adapter = new PrismaNeonHTTP(neon(process.env.DATABASE_URL!))
const prisma = new PrismaClient({ adapter })
他に PrismaPlanetScale, PrismaD1(Cloudflare D1), PrismaPg, PrismaLibSQL など。
Read Replica(読み取り専用 DB)
npm install @prisma/extension-read-replicas
import { PrismaClient } from "@prisma/client"
import { readReplicas } from "@prisma/extension-read-replicas"
const prisma = new PrismaClient().$extends(
readReplicas({ url: process.env.DATABASE_REPLICA_URL! })
)
// 自動的に SELECT は replica へ
await prisma.post.findMany()
// 強制的に primary
await prisma.$primary().post.findMany()
ロギングとメトリクス
クエリログ
const prisma = new PrismaClient({
log: [
{ emit: "event", level: "query" },
{ emit: "event", level: "warn" },
]
})
prisma.$on("query", (e) => {
if (e.duration > 100) {
console.warn(`SLOW QUERY ${e.duration}ms`, e.query, e.params)
}
})
メトリクス(Prometheus)
generator client {
provider = "prisma-client-js"
previewFeatures = ["metrics"]
}
const m = await prisma.$metrics.prometheus()
// /metrics エンドポイントで配信
Tracing(OpenTelemetry)
generator client {
previewFeatures = ["tracing"]
}
JSON Schema 整合(Zod 連携)
外部入力を Zod で検証 → Prisma に渡すパターン。zod-prisma-types 等のジェネレータで自動化。
import { z } from "zod"
const NewPost = z.object({
title: z.string().min(1).max(200),
content: z.string().nullable(),
})
const data = NewPost.parse(input)
await prisma.post.create({ data })
大量データの処理
chunk 化バッチ
async function batchCreate(items: any[], chunk = 1000) {
for (let i = 0; i < items.length; i += chunk) {
await prisma.post.createMany({
data: items.slice(i, i + chunk),
skipDuplicates: true,
})
}
}
cursor stream
let cursor: string | undefined
while (true) {
const batch = await prisma.post.findMany({
take: 1000,
...(cursor && { skip: 1, cursor: { id: cursor } }),
orderBy: { id: "asc" },
})
if (batch.length === 0) break
for (const post of batch) {
// 処理
}
cursor = batch[batch.length - 1].id
}
カスタムタイプ(Decimal / BigInt)
- Decimal:
Prisma.Decimalクラス。.toNumber()/.toString() - BigInt: JS ネイティブ。
JSON.stringify不可なので serializer 必要
BigInt の JSON 化
// グローバル拡張
;(BigInt.prototype as any).toJSON = function () { return this.toString() }
// あるいは replacer
JSON.stringify(obj, (_, v) => typeof v === "bigint" ? v.toString() : v)
マルチスキーマ・マルチ DB
- 1 PrismaClient で複数 DB は不可。複数 PrismaClient を作る
- マルチスキーマ(同じ DB の複数 schema)は preview で対応
Prisma Postgres(公式マネージド)
Prisma 公式が出しているPostgres マネージド。Accelerate 込みで Edge から低遅延。 Free tier あり。Vercel 等から 1 クリックで連携。
Pulse(Realtime)
DB 変更イベントを TypeScript 側で購読する公式拡張(CDC)。
const stream = await prisma.post.stream({ name: "post-stream" })
for await (const event of stream) {
// event.action: "create" | "update" | "delete"
console.log(event)
}
Prisma Studio のアクセス制御
Studio は本番でそのまま公開しない。社内ツール経由 / VPN / SSH トンネル越しに使う。
v6 / 新 Client(TypeScript エンジン)
- Prisma 6 でRust エンジンに代わる TypeScript 実装が登場
- バイナリ依存が減り、Edge / Worker への配置が容易
- provider = "prisma-client" で有効化(preview)
- 2026 中に正式リリース見込み
失敗パターン
| 症状 | 対処 |
|---|---|
| $queryRaw で型エラー | 明示的に型を指定(<Row[]>) |
| SQL Injection | テンプレートリテラルか Prisma.sql を使う |
| Edge で動かない | Driver Adapter / Accelerate を導入 |
| 巨大バンドル | output dir を node_modules 外に / dynamic import |
| $extends 後の型推論 | 新しいインスタンスを export し直す |
まず標準 API + $queryRaw + 楽観ロックで大半のニーズは満たせる。 $extends / Accelerate / Pulse は必要になってから導入する。早すぎる最適化は禁物。