忘れられないトラブル:PrismaがサーバーのRAMを1GBも食いつぶした話
深夜2時、スマホが激しく振動しました。モニタリングシステムが赤色のアラートを出しており、Node.jsインスタンスのメモリ使用率が95%に急上昇、レイテンシは5秒に達していました。ログを調査した結果、犯人はPrisma Clientであることが判明しました。50以上のテーブルを持つスキーマと、入れ子になったincludeクエリにより, Rustで書かれたPrismaエンジンがデータマッピングのために膨大なリソースを消費していたのです。
5人の開発者が参加する次のプロジェクトでは、スタックを完全に入れ替えることにしました。重厚なORMを排除し、Kyselyへと切り替えたのです。結果は驚くべきものでした。チームの生産性は明らかに向上し、コードの実行速度も上がり、何よりデータベース関連のランタイムエラーがほぼ皆無になりました。
なぜPrismaやTypeORMが足かせになりつつあるのか?
PrismaはSQLの複雑さを隠してくれるため、初心者には最適です。しかし、プロジェクトがスケールするにつれて、以下のような現実的な問題に直面することになります。
- 実行時のオーバーヘッド: Prismaはサイドカープロセスとして独自のクエリエンジンを実行します。AWS Lambdaでは、これによりコールドスタートが200msから2〜3秒にまで増加してしまいます。
- 最適化の難しさ: Prismaで共通テーブル式(CTE)やウィンドウ関数を書くのは苦行です。結局
$queryRawに頼ることになり、型安全性のメリットが失われてしまいます。 - 型安全性の甘さ: TypeORMはデコレータに強く依存しています。
@Entity()を一つ忘れただけで、エラーは本番環境でコードが実行されるまで表面化しません。
Kysely – 純粋なSQLとTypeScriptの融合
KyselyはORMではありません。型安全なSQLクエリビルダーです。データベースの行を複雑なオブジェクトに変換しようとするのではなく、TypeScriptで直接SQLを書くことを支援します。
Kyselyの最大の利点は、実行時のオーバーヘッドがゼロ(Zero Runtime Overhead)であることです。これは単に賢いSQL文字列構築ツールに過ぎません。Prismaエンジンが数十MBあるのに対し、Kyselyのバンドルサイズはわずか数十KBです。
実践的なインストールと設定
PostgreSQLを使用する場合、以下のパッケージをインストールします:
npm install kysely pg
npm install -D @types/pg
ORMの推論に任せるのではなく、インターフェースを通じてテーブル構造を明確に定義します。これがアプリ全体の「唯一の真実(Source of Truth)」となります:
import { Generated, ColumnType } from 'kysely'
interface UserTable {
id: Generated<number>
email: string
first_name: string | null
created_at: ColumnType<Date, string | undefined, never>
}
export interface Database {
users: UserTable
posts: { id: Generated<number>; title: string; author_id: number }
}
現場の知恵: テーブルが100個もあるなら、手入力はやめましょう。kysely-codegenを使えば、DBスキーマをスキャンして一瞬でこれらのインターフェースを自動生成できます。
Kyselyインスタンスの初期化
接続設定は非常にシンプルです:
import { Pool } from 'pg'
import { Kysely, PostgresDialect } from 'kysely'
import { Database } from './types'
const db = new Kysely<Database>({
dialect: new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
}),
})
コーディング体験:TypeScriptがSQLを完全に理解する瞬間
本当の威力は、db.selectFrom(...)と入力したときに現れます。IDEがテーブル名、カラム名、そして対応するデータ型を正確に補完してくれます。カラム名のタイポによるエラーに悩まされることはもうありません。
複雑なSelectとJoin의 例
async function getPostsWithAuthors() {
return await db
.selectFrom('posts')
.innerJoin('users', 'users.id', 'posts.author_id')
.select([
'posts.id',
'posts.title',
'users.first_name as author_name'
])
.where('posts.title', 'like', '%TypeScript%')
.execute()
}
DBでカラム名を変更してもインターフェースを更新し忘れた場合、TypeScriptが即座にエラーを表示します。デプロイ前にコードが正しく動くことを確信できるのです。
なぜプロダクションプロジェクトにKyselyを選んだのか?
6ヶ月間の実運用を経て、3つの大きなメリットを実感しました:
- 完全なコントロール: どのSQLが実行されているかを正確に把握できます。インデックスの最適化やN+1問題の解決が、かつてないほど透明化されます。
- 実行速度: 複雑な中間層を通さず、クエリを直接実行します。Prismaと比較して、レイテンシが平均30%削減されました。
- 高度なSQLサポート: PostgresのJSONB、ウィンドウ関数、サブクエリなどを、安全でない生の文字列を使わずに非常にうまく処理できます。
まとめ:いつ移行すべきか?
スピード重視の小規模プロジェクト(MVP)であれば、Prismaは依然として優れた選択肢です。しかし、高負荷システムやサーバーレス環境を構築する場合、あるいはソースコードを完全に制御したい場合は、Kyselyは外せない選択肢となります。
最初の15分の設定時間を投資するだけで、深夜の呼び出しを回避できるようになります。Kyselyは、SQLのパワーとTypeScriptの安全性が理想的に交差するポイントなのです。

