追記 2024年10月1日
Update: finishing pull and push logic for policies and roles and preparing for beta release
更新: ポリシーとロールのプルとプッシュのロジックを完成させ、ベータ版リリースの準備をする。
追記終了
Star History
DrizzleのこのStarの伸びがこのORMに注目した理由です。
Star数
10,185 2023年6月17日
Repository
drizzle-team/drizzle-orm: TypeScript ORM that feels like writing SQL
DrizzleORM 公式サイト
DrizzleORM - next gen TypeScript ORM
Drizzleとは?
Drizzle ORMは、最大限の型安全性を考慮して設計されたSQLデータベースのためのTypeScript ORMです。SQLマイグレーションを自動生成するためのdrizzle-kit CLIコンパニオンが付属しています。
Drizzle ORMは、フレームワークではなく、ライブラリであることを意図しています。どのようなレベルであっても、常にオプトインソリューションであることに変わりはありません。
ORMの主な哲学は、「SQLが分かればDrizzle ORMも分かる」です。可能な限りSQLのような構文に従い、強い型付けを行い、実行時ではなくコンパイル時に失敗するようにしています。
drizzle 単語の意味
- 自動 霧雨が降る、小雨が降る、細かい水でぬらす
- 他動 〔~を〕霧雨のように降らせる
〔~を〕細い線のようにかける - 名 霧雨、こぬか雨、小雨、シトシト降る雨
特徴
- 完全な型安全性
- スマートな自動マイグレーション生成
- ORMの学習曲線がない
- テーブル定義とクエリのためのSQLライクなシンタックス
- クラス最高の完全型付きジョイン
- あらゆる複雑さの完全型付けされた部分選択と非部分選択
- 選択と挿入を別々に行うDBモデルのためのTS型の自動インファリング
- Zodスキーマ生成
- 依存関係ゼロ
I Have A New Favorite Database Tool
I Have A New Favorite Database Tool - YouTube
要約
Prismaの利用には多くの制約があるため、代替手段としてdrizzle ORMを紹介しています。drizzleは、SQLデータベースのためのTypeScript ORMであり、完全な型安全性を提供し、自動マイグレーション生成機能を備えています。また、カスタムリレーションを定義することができ、SQLクエリを1つだけ実行するため、非常に高速であることが特徴です。
drizzleの新しいリリースでは、Prismaに似たリレーショナルクエリが導入され、SQLの知識がなくても簡単に使用できるようになりました。また、drizzleは、常に1つのSQLクエリを実行するため、非常に高速であることが特徴です。drizzleの開発チームは、ドキュメントの提供や、開発者コミュニティの参加を促進しており、drizzleの利用を推奨しています。
対応するデータベース
Database | Support | ||
---|---|---|---|
PostgreSQL | ✅ | Docs | |
MySQL | ✅ | Docs | |
SQLite | ✅ | Docs | |
Cloudflare D1 | ✅ | Docs | Website |
libSQL | ✅ | Docs | Website |
Turso | ✅ | Docs | Website |
PlanetScale | ✅ | Docs | Website |
Neon | ✅ | Docs | Website |
Vercel Postgres | ✅ | Docs | Website |
Supabase | ✅ | Docs | Website |
DynamoDB | ⏳ | ||
MS SQL | ⏳ | ||
CockroachDB | ⏳ |
チュートリアルBlog
Drizzle ORMの使い方を理解するためのチュートリアル | アールエフェクト
という記事が見つかったので
現状のORMは?Drizzleの立ち位置はどうなっているんだ?という事が出発点になっています。
↑チュートリアルBlogを調べたときのメモ (長い)
環境の構築
インストール
mkdir drizzle
cd drizzle
npm init -y
npm install typescript ts-node @types/node --save-dev
npx tsc --init
TypeScript を利用するための設定は完了です。
データベースには SQLite を利用しています。
Drizzle のインストール
npm install drizzle-orm better-sqlite3
npm install --save-dev @types/better-sqlite3
npm install -D drizzle-kit
drizzle-kitは定義したスキーマファイルを利用して
テーブルを作成/更新するために必要な
マイグレーションファイルを作成するためのツールです。
node_modules/
package-lock.json
Drizzle の設定
スキーマファイルやデータベースの接続用のコードを
保存するためのプロジェクトディレクトリ直下に
dbディレクトリの作成を行います。
mkdir db
スキーマファイルの作成
スキーマファイルはDBを設計するためのファイルであり、
全ての設計を一つにまとめたり
機能毎に複数のファイルに分けて書いたりすることが出来ます。
dbディレクトリにschema.tsファイルを作成し、
todosテーブルをSQLiteデータベースに
作成するために以下のスキーマを設定します。
touch db/schema.ts
import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core';
export const todos = sqliteTable('todos', {
id: integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true }),
name: text('name'),
isCompleted: integer('isCompleted'),
});
Migration ファイルの作成
Drizzle Kitを利用して
スキーマファイルに記述した内容を元にマイグレーションファイルを作成します。
実行する際には—schemaオプションschema.tsファイルを指定します。
実行するとdrizzleディレクトリが作成され、
mataディレクトリとsqlファイルが作成されます。
ファイル名は自動で命名されます。
metaディレクトリの中には
マイグレーションを管理する情報が保存されます。
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
このコマンドを実行しただけで
データベース、テーブルが作成されるわけではありません。
SQL文が出力される
drizzle\0000_slow_martin_li.sql
名前は実行毎に変わる
CREATE TABLE `todos` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text,
`isCompleted` integer
);
ファイルの拡張子がsqlという名前がつけている通り
↑0000_amazing_firestar.sqlの中には
DDL(DataDefinitionLanguage)が記述されており
テーブル作成に利用することができます。
SQLのcreate文が記述されています。
create文なのでそのままSQLiteに接続して実行すれば
そのままテーブルを作成することができます。
DB への接続
データベースへの接続に利用するためのコードを記述するため
db.tsファイルをdbディレクトリの下に作成します。
touch db\db.ts
touch db\db.ts
touch db\db.ts
import { drizzle, BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
import Database from 'better-sqlite3';
const sqlite = new Database('./db/sqlite.db');
export const db: BetterSQLite3Database = drizzle(sqlite);
migrate(db, { migrationsFolder: './drizzle' });
最後の行に migrate関数 が記述されていますが
この1行がマイグレーションファイルを元に
データベースの作成やテーブルの作成/更新などを行います。
テーブルの構成を変更した場合など必要な時にのみ実行されます。
migrationFolderプロパティに
マイグレーションファイルが
保存されているdrizzleディレクトリを指定します。
SQLiteは先ほど説明した通りファイルベースなので
sqlite.dbという名前で
dbディレクトリに保存されるように設定しています。
Drizzle の動作確認
これまでに作成した情報を利用して
データベースのテーブルを操作するコードを記述するため
プロジェクトディレクトリ直下にindex.tsファイルを作成します。
index.tsファイルではSQLiteデータベースにアクセスを行う
todosテーブルからデータを取得します。
db.select().from(todos)のfromメソッドには
データを取得していテーブルの情報を指定しています。
todosはschema.tsファイルから import しています。
すべてのデータを取得するためallメソッドを
設定しています。
touch index.ts
touch index.ts
touch index.ts
import { db } from './db/db';
import { todos } from './db/schema';
function main() {
const allTodo = db.select().from(todos).all();
console.log(allTodo);
}
main();
作成した index.ts ファイルを実行します。
npx ts-node index.ts
npx ts-node index.ts
npx ts-node index.ts
実行しても todos テーブルには
何もデータが入っていないため空の配列が表示されます。
結果
[]
実行後dbディレクトリを確認すると
sqlite.dbファイルが作成されていることがわかります。
データの取得方法 (この時点ではDBに各種データは未登録)
allで
出力された10件分のデータを取得したい場合には
limitメソッドを利用することができます。
const allTodo = db.select().from(todos).liimit().all();
all メソッドで取得したデータ件数は length をつけて確認することができます。
const allTodo = db.select().from(todos).all().length();
get メソッドを利用すると 1 件のデータを取得することができます。
const singleTodo = db.select().from(todos).get();
//
const singleTodo = db.select().from(todos).limit().get();
データの登録
insert メソッドを利用してテーブルへ、データの登録を行います。
実行するためには run メソッドも必要です。
import { db } from "./db/db"
import { todos } from "./db/schema"
async function main() {
const result = db.insert(todos).values({ name: "VNS.BLUE", isCompleted: 0 }).run()
console.log("result", result)
const allTodo = db.select().from(todos).all()
console.log("allTodo", allTodo)
}
main()
npx ts-node index.ts
npx ts-node index.ts
npx ts-node index.ts
06-15 21:41:58> npx ts-node index.ts
result { changes: 1, lastInsertRowid: 1 }
allTodo [ { id: 1, name: 'Learn Drizzle', isCompleted: 0 } ]
DrizzleORMを利用してSQLiteデータベースへ
データが登録できるようになりました。
2回実行すると
2回登録されます。
同じデータでも上書きされずに、
追加されます。
マイグレーションの動作確認
スキーマファイルの変更を行った場合の
データベースのテーブルに反映させるための方法を確認してみます。
列の追加
ユーザの ID を保存するために todos のスキーマに userId 列を追加します。
import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core';
export const todos = sqliteTable('todos', {
id: integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true }),
name: text('name'),
userId: text('useId'), <<<<追記
isCompleted: integer('isCompleted'),
});
schema.ts ファイルを更新後、drizzle-kit を利用して
マイグレーションファイルを作成を行います。
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
新たに drizzle ディレクトリに 0001_flippant_sway.sql ファイルが作成されます。
中身を確認すると追加した date 列に関する alter table 文が記述されています。
ALTER TABLE todos ADD useId
text;
マイグレーションがテーブルに反映されるのか
確認するために select を実行します。
import { db } from './db/db';
import { todos } from './db/schema';
async function main() {
const allTodo = db.select().from(todos).all();
console.log('allTodo', allTodo);
}
main();
npx ts-node index.ts
npx ts-node index.ts
npx ts-node index.ts
取得したデータに userId 列が追加されていることが確認できます。
値には null が設定されています。
% npx ts-node index.ts
allTodo [ { id: 1, name: 'Learn Drizzle', userId: null, isCompleted: 0 } ]
列の削除
列の追加を行うことができたので次は追加した列を
削除したい場合の動作確認を行います。
追加したuserId列を削除するために
スキーマファイルを更新します。
import { text, integer, sqliteTable } from "drizzle-orm/sqlite-core"
export const todos = sqliteTable("todos", {
id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
name: text("name"),
isCompleted: integer("isCompleted"),
})
schema.ts ファイルを更新後、マイグレーションファイルを作成を行います。
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
06-15 21:51:30> npx drizzle-kit generate:sqlite --schema=./db/schema.ts
drizzle-kit: v0.18.1
drizzle-orm: v0.26.5
1 tables
todos 3 columns 0 indexes 0 fks
[✓] Your SQL migration file ➜ drizzle\0002_third_molecule_man.sql 🚀
drizzle\0002_third_molecule_man.sqlファイルには列の削除を行う
alter table 文が記述されています。
ALTER TABLE `todos` DROP COLUMN `useId`;
マイグレーションの内容を反映させるために
index.tsファイルを実行します。
実行すると以下のuserIdが削除されていることが
確認できます。
npx ts-node index.ts
npx ts-node index.ts
npx ts-node index.ts
% npx ts-node index.ts
08-07 18:49:55> npx ts-node index.ts
allTodo [
{ id: 1, name: 'Learn Drizzle', userId: null, isCompleted: 0 },
{ id: 2, name: 'VNS.BLUE', userId: null, isCompleted: 0 },
{ id: 3, name: 'VNS.BLUE', userId: null, isCompleted: 0 }
]
↓
08-07 18:55:05> npx ts-node index.ts
allTodo [
{ id: 1, name: 'Learn Drizzle', isCompleted: 0 },
{ id: 2, name: 'VNS.BLUE', isCompleted: 0 },
{ id: 3, name: 'VNS.BLUE', isCompleted: 0 }
]
userId: null の項目が消えているのが確認できた。
userId: null の項目が消えているのが確認できた。
userId: null の項目が消えているのが確認できた。
前回はここでBlogの動作と違っていて、出来ていなかった。
Drop Migration
DrizzleKit には DropMigration を行うコマンドがあります。
--outオプションにはマイグレーションディレクトリを指定します。
実行するとこれまで作成したマイグレーションの名前が表示されます。
選択することでマイグレーションファイルを削除することはできますが
削除してもデータベースのテーブルに反映されるわけではありません。
npx drizzle-kit drop --out ./drizzle
npx drizzle-kit drop --out ./drizzle
npx drizzle-kit drop --out ./drizzle
マイグレーションファイルを削除できた。
マイグレーションファイルを削除できた。
マイグレーションファイルを削除できた。
その他
設定ファイル
Drizzle Kit では設定ファイルを利用することができます。
drizzle.config.ts ファイルをプロジェクトフォルダ直下に作成して
スキーマフォルダやマイグレーションフォルダを指定することができます。
touch drizzle.config.ts
touch drizzle.config.ts
touch drizzle.config.ts
import type { Config } from 'drizzle-kit';
export default {
schema: './db/schema.ts',
out: './drizzle',
} satisfies Config;
drizzle.config.ts ファイルを作成後は
npx drizzle-kit generate
を実行する際にオプションに--schema を設定していましたが
schema の値は drizzle.config.ts ファイルに
記述されているため省略することができます。
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
npx drizzle-kit generate:sqlite --schema=./db/schema.ts
が
npx drizzle-kit generate:sqlite
npx drizzle-kit generate:sqlite
npx drizzle-kit generate:sqlite
で大丈夫になった
Type の設定(InferModel)
index.tsファイルの中でinsertの処理を
別の関数insertTodoに分けた場合に
引数に型をしない場合には todo の下に 赤線が引かれます。
引数に型をしない場合には todo の下に 赤線が引かれます。
引数に型をしない場合には todo の下に 赤線が引かれます。
import { db } from "./db/db"
import { todos } from "./db/schema"
// 引数に型をしない場合には todo の下に 赤線が引かれます。
const insertTodo = (todo) => {
return db.insert(todos).values(todo).run()
}
async function main() {
const result = insertTodo({ name: "Learn TypeScript", isCompleted: 0 })
console.log("result", result)
const allTodo = db.select().from(todos).all()
console.log(allTodo)
}
↑このソースにこのように↓型を追加すると、
todo の下についた赤線が消えます。
import { db } from './db/db';
import { todos } from './db/schema';
import { InferModel } from 'drizzle-orm';
type InsertTodo = InferModel<typeof todos, 'insert'>;
const insertTodo = (todo: InsertTodo) => {
return db.insert(todos).values(todo).run();
};
async function main() {
const result = insertTodo({ name: 'Learn TypeScript', isCompleted: 0 });
console.log('result', result);
const allTodo = db.select().from(todos).all();
console.log(allTodo);
}
これまではどのORMでも付属している機能でした。
次の項目からは、
ここまで来てようやくDrizzleを使うメリットがでてきます。
型の設定に関するメッセージ
型を設定するために InferModel を利用することができます。
InforModeを利用して作成した型 InsertTodo を
insertTodo の引数の todo の型として利用することができます。
type InsertTodo = InferModel<typeof todos, 'insert'>;
type Todo = InferModel<typeof todos, "select">
型の上にマウスカーソルを持ってくると、
型を見ることが出来ます。
InsertTodo型とTodo型は、drizzle-ormモジュールのInferModel型を使用して、todosスキーマに対するinsert操作とselect操作の型を推論しています。InferModel型は、テーブルスキーマと操作名を引数に取り、その操作に対する型を推論します。
typeof todosは、todosスキーマオブジェクトの型を取得します。"insert"と"select"は、それぞれtodosスキーマに対するinsert操作とselect操作を表します。InferModel型は、これらの引数を使用して、todosスキーマに対するinsert操作とselect操作の型を推論します。
したがって、InsertTodo型とTodo型は、todosスキーマに対するinsert操作とselect操作の型を表します。
selectから取得する値に対する型が必要な場合にも
InforMode を利用することができます。
import { db } from "./db/db"
import { todos } from "./db/schema"
import { InferModel } from "drizzle-orm"
type InsertTodo = InferModel<typeof todos, "insert">
type Todo = InferModel<typeof todos, "select">
const insertTodo = (todo: InsertTodo) => {
return db.insert(todos).values(todo).run()
}
async function main() {
const result = insertTodo({ name: "VNS.BLUE", isCompleted: 0 })
console.log("result", result)
const allTodo: Todo[] = db.select().from(todos).all()
console.log(allTodo)
}
main()
このコードは、TypeScriptファイルで、dbというデータベースオブジェクトと、todosというスキーマオブジェクトをdbモジュールからインポートしています。また、drizzle-ormモジュールからInferModel型をインポートしています。
InsertTodo型は、InferModel型を使用して、todosスキーマに対するinsert操作の型を推論するために使用されます。Todo型は、InferModel型を使用して、todosスキーマに対するselect操作の型を推論するために使用されます。
insertTodo関数は、InsertTodo型のtodoオブジェクトを引数に取り、db.insertメソッドを使用してtodosテーブルに挿入します。
main関数は、insertTodo関数を呼び出し、nameプロパティが"VNS.BLUE"に設定され、isCompletedプロパティが0に設定されたオブジェクトを渡します。insertTodo関数の結果はコンソールにログ出力されます。
allTodo変数は、db.selectメソッドを使用してtodosテーブルからすべての行を選択するselectクエリの結果を代入します。クエリの結果はコンソールにログ出力されます。
注意点としては、このコードは、drizzle-ormモジュールを使用しているため、drizzle-ormモジュールがインストールされている必要があります。
実行した SQL の中身
実行した SQL の中身を確認したい場合には
toSQL メソッドを利用することができます。
import { db } from './db/db';
import { todos } from './db/schema';
async function main() {
const query = db.select().from(todos).toSQL();
console.log(query);
}
main();
index.ts を実行すると実行した SQL が
'select "id", "name", "isCompleted" from "todos"'
であることがわかります。
% npx ts-node index.ts
{ sql: 'select "id", "name", "isCompleted" from "todos"', params: [] }
Logging の設定
実行した SQL の中身を確認するために logging の設定を行うことができます。
import { drizzle, BetterSQLite3Database } from "drizzle-orm/better-sqlite3"
import { migrate } from "drizzle-orm/better-sqlite3/migrator"
import Database from "better-sqlite3"
const sqlite = new Database("./db/sqlite.db")
// export const db: BetterSQLite3Database = drizzle(sqlite)
// ↓ ログを取る
export const db: BetterSQLite3Database = drizzle(sqlite, { logger: true })
migrate(db, { migrationsFolder: "./drizzle" })
下記の index.ts ファイルを実行します。
import { db } from './db/db';
import { todos } from './db/schema';
async function main() {
const query = db.select().from(todos).toSQL();
console.log(query);
}
main();
npx ts-node index.ts
npx ts-node index.ts
npx ts-node index.ts
↓ログが取得できています。
08-07 20:07:28> npx ts-node index.ts
Query:
CREATE TABLE IF NOT EXISTS "__drizzle_migrations" (
id SERIAL PRIMARY KEY,
hash text NOT NULL,
created_at numeric
)
Query: SELECT id, hash, created_at FROM "__drizzle_migrations" ORDER BY created_at DESC LIMIT 1
Query: BEGIN
Query: COMMIT
{ sql: 'select "id", "name", "isCompleted" from "todos"', params: [] }
npm trends
ORM一覧 (2023/06/15現在)
ORM | Star | GitHubリポジトリ | 公式サイト | ドキュメント | 説明 |
---|---|---|---|---|---|
Prisma | 32000 | prisma | 公式 | Doc | Node.jsとTypeScript向けの次世代ORMです。型安全で、データベーススキーマをコードで管理できます。また、Prismaは、データベースクエリを生成するために、自動的にSQLを生成するため、開発者はSQLを直接書く必要がありません。 |
TypeORM | 31400 | typeorm | 公式 | Doc | TypeScriptで書かれたNode.js向けのORMです。TypeORMは、Active RecordパターンとData Mapperパターンの両方をサポートしています。また、TypeORMは、データベーススキーマをコードで管理することができます。 |
Sequelize | 28000 | sequelize | 公式 | Doc | PromiseベースのNode.js ORMです。MySQL、MariaDB、SQLite、Microsoft SQLServer、Postgresで使用できます。Sequelizeは機能が多く人気があります。他のORMに比べてドキュメントも豊富です。 |
Mongoose | 25700 | mongoose | 公式 | Doc | MongoDB用のNode.js ORMです。Mongooseは、MongoDBのドキュメント指向データベースに対してObject Document Mapping(ODM)を提供します。Mongooseは、MongoDBのドキュメント指向データベースに対してObject Document Mapping(ODM)を提供します。 |
Drizzle | 10100 | drizzle-orm | 公式 | Doc | Node.js向けのORMです。Drizzleは、MySQLとPostgreSQLに対応しています。Drizzleは、シンプルなAPIと高速なパフォーマンスを提供します。 |
Objection.js | 7000 | objection.js | 公式 | Doc | Node.js向けのORMです。Objection.jsは、Active RecordパターンとData Mapperパターンの両方をサポートしています。Objection.jsは、PostgreSQL、MySQL、SQLite3などのデータベースに対応しています。 |
Bookshelf | 6300 | bookshelf | 公式 | Doc | Node.js向けのORMです。Bookshelfは、Knex Query Builderを基盤としています。Bookshelfは、Active RecordパターンとData Mapperパターンの両方をサポートしています。 |
Drizzleの歴史?
MySQL闇歴史 Drizzle - Qiita
https://qiita.com/RKajiyama/items/08fb46fb0d8b5ed6420d
MySQL 6.0で実現できなかった機能の実装やアーキテクチャの見直しを行ったオープンソースのプロジェクトがDrizzleです。旧MySQL社のアーキテクチャだった人物や 当時元気 だったmixiのエンジニアなどが立ち上げ、2011年3月に正式リリース。
https://www.publickey1.jp/blog/11/mysqldrizzle_1.html
霧雨のように消えていった度:★★★★★
2013年末までには新規開発をしないメンテナンスモードとなり、2016年にプロジェクト終了となった。
調査結果
drizzle.org
http://drizzle.org/
を訪ねてみると、
このドメイン名はGandi.netで登録されています。
drizzle.orgのWhois情報にアクセスして、ドメイン名の登録状況を確認しましょう。
とあったので、名前が同じで別のプロジェクトのようです。
Star Historyを見ると2023年の3月頃に急に増えているので、その頃になにかあったのかもしれません。