Kysely:TypeScriptプロジェクトでPrismaやTypeORMに代わるクエリビルダーの決定版

Development tutorial - IT technology blog
Development tutorial - IT technology blog

忘れられないトラブル: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つの大きなメリットを実感しました:

  1. 完全なコントロール: どのSQLが実行されているかを正確に把握できます。インデックスの最適化やN+1問題の解決が、かつてないほど透明化されます。
  2. 実行速度: 複雑な中間層を通さず、クエリを直接実行します。Prismaと比較して、レイテンシが平均30%削減されました。
  3. 高度なSQLサポート: PostgresのJSONB、ウィンドウ関数、サブクエリなどを、安全でない生の文字列を使わずに非常にうまく処理できます。

まとめ:いつ移行すべきか?

スピード重視の小規模プロジェクト(MVP)であれば、Prismaは依然として優れた選択肢です。しかし、高負荷システムやサーバーレス環境を構築する場合、あるいはソースコードを完全に制御したい場合は、Kyselyは外せない選択肢となります。

最初の15分の設定時間を投資するだけで、深夜の呼び出しを回避できるようになります。Kyselyは、SQLのパワーとTypeScriptの安全性が理想的に交差するポイントなのです。

Share: