13
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ARISE analyticsAdvent Calendar 2023

Day 25

TypeScriptでの良さげなORM見つけた。その名もDrizzle

Last updated at Posted at 2023-12-24

Drizzle ORMの紹介

TypeScriptでORMを使うときに何を使いますか?

本記事では筆者が最近使っているDrizzle ORMの紹介をしていきます。

特徴

Using Drizzle you can define & manage database schemas in typescript, access your data in a SQL-like or relational way, and take advantage of opt in tools to push your developer experience through the roof

Drizzleの公式ページの言葉を見るとこのように書かれています。

TypeScriptで型とスキーマの定義が可能で、SQLとリレーショナル(他のORMでよくある方法?)な手法でデータにアクセスできる。

早速次からどのように書くか見てましょう。

サンプル

今回はPostgreSQLを使った例を示します。

スキーマ定義

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

// declaring enum in database
export const popularityEnum = pgEnum('popularity', ['unknown', 'known', 'popular']);

export const countries = pgTable('countries', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 256 }),
}, (countries) => {
  return {
    nameIndex: uniqueIndex('name_idx').on(countries.name),
  }
});

export const countriesRelations = relations(countries, ({ many }) => ({
	cities: many(cities),
}));

export const cities = pgTable('cities', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 256 }),
  countryId: integer('country_id').references(() => countries.id),
  popularity: popularityEnum('popularity'),
});

pgTableという関数を使ってテーブル名とテーブル定義をオブジェクトで受け取っていますね。ここはTypeScriptみを感じますね。

countriesRelationsについてはリレーショナルな書き方をする際に登場します。

外部キーを指定する場合はオブジェクトのvalue内に references メソッドで定義が可能です。

またインデックスキーは第三引数でコールバック関数として返すようです。

このテーブル定義のselectで得られる型については以下で抽出可能。

xport type User = typeof countries.$inferSelect;

データ取得

冒頭に書いたSQLライクな書き方と、リレーショナルな書き方をやってみます。

まずはSQLライクな書き方

import { eq } from 'drizzle-orm';

const result = await db.select().from(countries).where(eq(countries.id, 42));

見たままSQLですね。これはSQLに慣れたエンジニアには嬉しいですね。

一方でリレーショナルな書き方

import * as schema from './schema';
import { drizzle } from 'drizzle-orm/...';

const db = drizzle(client, { schema });

const result = await db.query.countries.findMany({
	with: {
		cities: true
	},
});

こちらはDBコネクションのタイミングでDBのスキーマをschemaとして渡す必要があります。

実際にデータにアクセスするためには db.query.{テーブル}.findMany(~) と書くことでクエリを発行できます。

更に with が続いていますが、ここでスキーマ定義した countriesRelations が効果を発揮します。

この書き方をするとリレーション関係にあるレコードを取得することができます。

おまけでマイグレーション

Drizzleはマイグレーション機能も提供しているのでおまけとして書きます。

設定ファイル

drizzle.config.ts
import 'dotenv/config';
import type { Config } from 'drizzle-kit';

export default {
	schema: './src/schema.ts',
	out: './drizzle/migrations',
	driver: 'pg',
	dbCredentials: {
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
	},
} satisfies Config;
src/db.ts
import { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise';
import * as schema from './schema';

export const connection = mysql.createConnection({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  multipleStatements: true,
});

export const db = drizzle(connection, { schema });
src/migrate.ts
import 'dotenv/config';
import { migrate } from 'drizzle-orm/mysql2/migrator';
import { db, connection } from './db';

// This will run migrations on the database, skipping the ones already applied
await migrate(db, { migrationsFolder: './drizzle' });

// Don't forget to close the connection, otherwise the script will hang
await connection.end();

このsrc/migrate.tsをtscで実行するとマイグレーションが実行されます。

今までPrisma一択で新しいORMを探している方はぜひ!

13
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?