Documentation Drizzle

概要

この記事では、Drizzle ORMを使ってPostgreSQLのカラム型を指定する方法を解説します。PostgreSQLはさまざまなデータ型を提供しており、それぞれの型を適切に選択することで、データベースの効率性とパフォーマンスが向上します。Drizzle ORMのsqlColumnメソッドを使って、データ型を指定し、アプリケーションの要件に合ったデータ管理を行いましょう。

Drizzle ORMとPostgreSQLのカラム型の概要

Drizzle ORMは、型安全なORMであり、PostgreSQLなどのデータベースで使用するカラム型を定義できます。各カラム型には、データの保存方法やサイズ、パフォーマンスに影響を与える特性があります。データ型を正しく選択することで、データの整合性やクエリの効率性を高めることが可能です。 PostgreSQLのデータ型は、主に以下のカテゴリに分類されます:

  • 数値型
  • 文字列型
  • 論理型
  • 日時型
  • 配列型
  • JSON型 Drizzle ORMを使ってこれらの型を効率的に指定し、データの適切な保存と管理を行いましょう。

Drizzle ORMで使用するPostgreSQLのカラム型

数値型

数値型は、整数や小数を扱うためのデータ型です。具体的には、int, bigint, smallint, numeric, decimal などがあります。

  • int:標準的な整数型で、主にIDやカウントに使われます。
  • bigint:非常に大きな整数を保存する場合に適しています。
  • numeric / decimal:小数点を含む数値を正確に表現したい場合に使用します。

Drizzle ORMでの数値型の定義例

import { pgTable, serial, integer, bigint, numeric, real } from 'drizzle-orm/pg-core';

const products = pgTable('products', {
  id: serial('id').primaryKey(),
  stock: integer('stock').notNull().default(0),
  views: bigint('views', { mode: 'number' }),
  price: numeric('price', { precision: 10, scale: 2 }).notNull(),
  weight: real('weight') // 浮動小数点数
});

この例では、以下の数値型を使用しています:

  • serial: 自動インクリメントのID
  • integer: 在庫数などの整数値
  • bigint: 大きな数値(閲覧数など)
  • numeric: 精度が重要な金額(小数点以下2桁)
  • real: 重量などの浮動小数点数

文字列型

文字列型は、テキストや短い文字列データを保存するための型です。text, varchar, char などのデータ型があります。

  • text:長いテキストデータを扱います。長さの制限がないため、柔軟に使えます。
  • varchar(n):指定した長さの文字列を保存。文字数に制限が必要な場合に使用します。
  • char(n):固定長の文字列を扱います。

Drizzle ORMでの文字列型の定義例

import { pgTable, serial, varchar, text, char } from 'drizzle-orm/pg-core';

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  username: varchar('username', { length: 50 }).notNull().unique(),
  email: varchar('email', { length: 255 }).notNull(),
  bio: text('bio'),
  status: char('status', { length: 1 }).default('A') // 'A'ctive, 'I'nactive
});

この例では、以下の文字列型を使用しています:

  • varchar(50): ユーザー名(最大50文字)
  • varchar(255): メールアドレス
  • text: 長い自己紹介文(長さ制限なし)
  • char(1): 固定長のステータスコード

論理型

論理型は、trueまたはfalseの値を保存するための型で、booleanが該当します。条件のオン・オフやフラグを管理するのに役立ちます。

Drizzle ORMでの論理型の定義例

import { pgTable, serial, varchar, boolean, timestamp } from 'drizzle-orm/pg-core';

const tasks = pgTable('tasks', {
  id: serial('id').primaryKey(),
  title: varchar('title', { length: 100 }).notNull(),
  completed: boolean('completed').default(false).notNull(),
  is_public: boolean('is_public').default(true),
  is_archived: boolean('is_archived').default(false),
  created_at: timestamp('created_at').defaultNow()
});

この例では、boolean型を複数のフラグ管理に使用しています:

  • completed: タスク完了状態(デフォルト: false)
  • is_public: 公開設定
  • is_archived: アーカイブ状態

日時型

日時型は、日時情報を扱うためのデータ型です。timestamp, date, time, timestamptz などのデータ型が提供されています。

  • timestamp:タイムゾーンなしで日時を表現。
  • timestamptz:タイムゾーン付きで日時を表現し、グローバルな時間管理に最適です。
  • date:日付のみを保存する際に使用。

Drizzle ORMでの日時型の定義例

import { pgTable, serial, varchar, date, timestamp, time } from 'drizzle-orm/pg-core';

const events = pgTable('events', {
  id: serial('id').primaryKey(),
  event_name: varchar('event_name', { length: 100 }).notNull(),
  event_date: date('event_date', { mode: 'date' }), // Date オブジェクト
  event_time: time('event_time'), // 時刻のみ
  start_at: timestamp('start_at', { mode: 'date', withTimezone: true }),
  created_at: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
  updated_at: timestamp('updated_at', { mode: 'date' }).$onUpdate(() => new Date())
});

この例では、以下の日時型を使用しています:

  • date: イベント開催日(日付のみ)
  • time: イベント開始時刻(時刻のみ)
  • timestamp(withTimezone: true): タイムゾーン付き日時
  • timestamp().defaultNow(): 作成日時(自動設定)
  • $onUpdate(): 更新日時の自動更新

配列型

配列型は、同じデータ型の複数の値を一つのフィールドに格納したい場合に使用します。例えば、int[], text[] などがあります。

Drizzle ORMでの配列型の定義例

import { pgTable, serial, varchar, text, integer } from 'drizzle-orm/pg-core';

const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: varchar('title', { length: 200 }).notNull(),
  tags: text('tags').array(), // text[]
  category_ids: integer('category_ids').array(), // integer[]
  mentions: varchar('mentions', { length: 50 }).array() // varchar[]
});

// データの挿入例
// INSERT INTO posts (title, tags, category_ids)
// VALUES ('記事タイトル', ARRAY['typescript', 'drizzle'], ARRAY[1, 2, 3])

この例では、複数の配列型を使用しています:

  • text().array(): タグの文字列配列
  • integer().array(): カテゴリIDの数値配列
  • varchar().array(): メンション先の配列

配列型を使うことで、別テーブルを作らずに複数の値を1カラムで管理できます。

JSON型

JSON型は、構造化データを保存するために使用されるデータ型で、jsonjsonbの2種類があります。

  • json:保存されたデータはそのままの形式で保持されます。
  • jsonb:バイナリ形式で保存され、検索やフィルタリングが高速になります。

Drizzle ORMでのJSON型の定義例

import { pgTable, serial, varchar, json, jsonb } from 'drizzle-orm/pg-core';

const orders = pgTable('orders', {
  id: serial('id').primaryKey(),
  customer_name: varchar('customer_name', { length: 100 }).notNull(),
  // json: そのままの形式で保存(高速な書き込み)
  metadata: json('metadata'),
  // jsonb: バイナリ形式で保存(高速な検索・インデックス可能)
  order_details: jsonb('order_details').$type<{
    items: Array<{ productId: number; quantity: number; price: number }>;
    shipping: { address: string; method: string };
    discount?: { code: string; amount: number };
  }>()
});

// 型安全なデータ挿入例
// await db.insert(orders).values({
//   customer_name: '田中太郎',
//   order_details: {
//     items: [
//       { productId: 1, quantity: 2, price: 1000 },
//       { productId: 3, quantity: 1, price: 1500 }
//     ],
//     shipping: {
//       address: '東京都...',
//       method: '宅配便'
//     }
//   }
// });

この例では、JSON型の使い分けを示しています:

  • json: 読み取り専用のメタデータなど(書き込み重視)
  • jsonb: 検索・フィルタリングが必要な注文詳細(検索重視)
  • $type<T>(): TypeScriptの型安全性を追加

実践例:ECサイトのスキーマ定義

複数のカラム型を組み合わせた実践的な例として、ECサイトのスキーマを定義してみましょう。

import { pgTable, serial, varchar, text, integer, numeric, boolean, timestamp, jsonb } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';

// 商品テーブル
export const products = pgTable('products', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 200 }).notNull(),
  description: text('description'),
  price: numeric('price', { precision: 10, scale: 2 }).notNull(),
  stock: integer('stock').notNull().default(0),
  is_active: boolean('is_active').default(true),
  tags: text('tags').array(),
  specifications: jsonb('specifications').$type<{
    weight?: string;
    dimensions?: { width: number; height: number; depth: number };
    color?: string[];
    material?: string;
  }>(),
  created_at: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
  updated_at: timestamp('updated_at', { mode: 'date' }).$onUpdate(() => new Date())
});

// 注文テーブル
export const orders = pgTable('orders', {
  id: serial('id').primaryKey(),
  user_id: integer('user_id').notNull(),
  status: varchar('status', { length: 20 }).notNull().default('pending'),
  total_amount: numeric('total_amount', { precision: 12, scale: 2 }).notNull(),
  shipping_address: jsonb('shipping_address').$type<{
    postal_code: string;
    prefecture: string;
    city: string;
    address_line1: string;
    address_line2?: string;
  }>().notNull(),
  is_paid: boolean('is_paid').default(false),
  paid_at: timestamp('paid_at', { mode: 'date' }),
  created_at: timestamp('created_at', { mode: 'date' }).defaultNow().notNull()
});

// クエリ例
import { drizzle } from 'drizzle-orm/node-postgres';
import { eq, and, gte } from 'drizzle-orm';

const db = drizzle(/* ... */);

// 在庫ありの商品を検索
const activeProducts = await db
  .select()
  .from(products)
  .where(and(
    eq(products.is_active, true),
    gte(products.stock, 1)
  ));

// JSONBカラムを使った検索(PostgreSQL特有の演算子)
import { sql } from 'drizzle-orm';

const redProducts = await db
  .select()
  .from(products)
  .where(sql`${products.specifications}->>'color' = 'red'`);

この例では、実際のアプリケーションで使われる様々なカラム型を組み合わせています:

  • 数値型:価格(精度重視)、在庫数
  • 文字列型:商品名、ステータス
  • 論理型:公開状態、支払い済みフラグ
  • 日時型:作成日時、支払日時(自動更新)
  • 配列型:タグの管理
  • JSON型:構造化データ(仕様、配送先住所)

Drizzle ORMでPostgreSQLカラム型を選択するメリット

Drizzle ORMでPostgreSQLのカラム型を適切に選択することには、次のようなメリットがあります。

  1. 型安全性の確保
    TypeScriptと連携して型チェックが行われるため、データベースとアプリケーション間での型の不一致を防ぎます。
  2. データの整合性
    各カラム型に適したデータのみが保存されるため、データの一貫性と信頼性が向上します。
  3. パフォーマンスの最適化
    データ型を適切に選択することで、ストレージの効率化や検索の高速化が期待でき、データベースのパフォーマンスが向上します。
  4. クエリの簡素化
    JSONや配列型を利用することで、複雑なデータ構造を扱いやすくなり、シンプルなクエリで柔軟なデータ操作が可能になります。

まとめ

Drizzle ORMを使用してPostgreSQLのさまざまなカラム型を適切に選択することで、型安全性を確保しつつデータベースのパフォーマンスを向上させることができます。データ型を正しく設定することで、データの整合性やアプリケーションの信頼性が高まり、データベース管理がより効率的になります。Drizzle ORMでのデータ型設定は、データベースとアプリケーション間のスムーズなデータ連携を実現するための重要な要素です。

参考文献

円