概要
この記事では、Drizzle ORMを使ってPostgreSQLのカラム型を指定する方法を解説します。PostgreSQLはさまざまなデータ型を提供しており、それぞれの型を適切に選択することで、データベースの効率性とパフォーマンスが向上します。Drizzle ORMのsqlColumnメソッドを使って、データ型を指定し、アプリケーションの要件に合ったデータ管理を行いましょう。
Drizzle ORMとPostgreSQLのカラム型の概要
Drizzle ORMは、型安全なORMであり、PostgreSQLなどのデータベースで使用するカラム型を定義できます。各カラム型には、データの保存方法やサイズ、パフォーマンスに影響を与える特性があります。データ型を正しく選択することで、データの整合性やクエリの効率性を高めることが可能です。
PostgreSQLのデータ型は、主に以下のカテゴリに分類されます:
- 数値型
- 文字列型
- 論理型
- 日時型
- 配列型
- JSON型
DrizzleORMを使ってこれらの型を効率的に指定し、データの適切な保存と管理を行いましょう。
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: 自動インクリメントのIDinteger: 在庫数などの整数値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型は、構造化データを保存するために使用されるデータ型で、jsonとjsonbの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のカラム型を適切に選択することには、次のようなメリットがあります。
- 型安全性の確保
TypeScriptと連携して型チェックが行われるため、データベースとアプリケーション間での型の不一致を防ぎます。 - データの整合性
各カラム型に適したデータのみが保存されるため、データの一貫性と信頼性が向上します。 - パフォーマンスの最適化
データ型を適切に選択することで、ストレージの効率化や検索の高速化が期待でき、データベースのパフォーマンスが向上します。 - クエリの簡素化
JSONや配列型を利用することで、複雑なデータ構造を扱いやすくなり、シンプルなクエリで柔軟なデータ操作が可能になります。
まとめ
Drizzle ORMを使用してPostgreSQLのさまざまなカラム型を適切に選択することで、型安全性を確保しつつデータベースのパフォーマンスを向上させることができます。データ型を正しく設定することで、データの整合性やアプリケーションの信頼性が高まり、データベース管理がより効率的になります。Drizzle ORMでのデータ型設定は、データベースとアプリケーション間のスムーズなデータ連携を実現するための重要な要素です。