[ORM] drizzleとは?基本的なクエリまで
最初に
最近プロジェクトでdrizzleを使用する機会があったので、備忘録として残しておきます。
内容としては基本的な操作になります。
では始めます。
他のORMとの違いは?
CLI依存が少ない
例えば、prismaでは基本的に以下の流れで開発を進めると思います。
① スキーマを定義する
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}
② CLIを実行する
※ これやらないと、tsの補完がされないかつ、ci/cdで自動化している場合は忘れると型エラーに陥ってしまう。
npx prisma generate
③ クエリ書く
const user = await prisma.user.findUnique({
where: { id: 1 },
});
// user は `User | null` 型として推論される
しかし、drizzleだと、基本的にtsでスキーマを定義するので、書いた時点で型補完が効くようになります。
① スキーマを定義する
// db/schema.ts
import { pgTable, serial, varchar } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 255 }),
email: varchar('email', { length: 255 }),
});
② クエリ書く
import { db } from './db';
import { users } from './schema';
const result = await db.select().from(users);
// result: { id: number; name: string; email: string }[]
このように、スキーマ自体をts出かけることによる開発者体験のメリットはかなり高いと思います。
SQLにより近い形式でORMを扱える
他のORMやデータライブラリは、SQLの構文から離れ、抽象化する傾向にあるのに対し、
drizzle-ormは設計の中核をSQLにすることにより、他のORMを使用する際の学習コストが、SQLを理解している人であれば、かなり抑えられる。
例えば以下のように記述することができる。
import { eq } from 'drizzle-orm';
const users = await db.select(id: user.id, name: user.name).from(user).where(eq(user.id, 1)).orderBy(desc(user.createdAt));
これがprismaだと。。
const users = await prisma.user.findMany({
where: {
id: {
gt: 1
}
},
orderBy: {
createdAt: 'desc'
},
select: {
id: true,
name: true
}
});
このように、ORM独自の構文を理解しておく必要があるため、学習コストがdrizzleよりも気持ち高い傾向にあります。
しかし、drizzleもprismaライクな書き方はできるので、prismaに慣れている方であれば、drizzleへの移行も割と容易ではないでしょうか。
import { eq } from 'drizzle-orm';
const users = await this.db.query.user.findMany({
where: (table) => eq(table.id, 1),
columns: {
id: true,
name: true,
},
orderBy: (table, {desc}) => [desc(table.createdAt)],
});
他にもdrizzleはサーバレス設計のため、他のORMより軽量(connectionが軽い)である点等、多々ありますが、開発者が実際に恩恵を感じやすい点としては基本的に上記解説した箇所かなと思います。
※ 他にもあったら教えてください。
基本的なクエリ操作
基本的なクエリ操作の一覧を貼っておきます。
- get
const allUsers = await db.select().from(users);
- where句
import { eq } from 'drizzle-orm';
const result = await db.select().from(users).where(eq(users.email, 'test@example.com'));
- insert(単体・複数insert可能)
await db.insert(users).values({
name: 'Alice',
email: 'alice@example.com',
});
- insert(リターンあり)
const users = await db.insert(users).values({
name: 'Alice',
email: 'alice@example.com',
}).returning();
- upsert
await db.insert(users)
.values({ id: 1, name: 'Dan' })
.onConflictDoUpdate({ target: users.id, set: { name: 'John' } });
- conflict(競合があったらinsert NG)
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoNothing();
- update
await db.update(users)
.set({ name: 'Updated Name' })
.where(eq(users.id, 1));
- delete
await db.delete(users).where(eq(users.id, 1));
- delete(複数削除)
await db.delete(users).where(inArray(users.id, [1,2,3,4]));
- join
const result = await db
.select({
userName: users.name,
postTitle: posts.title,
})
.from(users)
.innerJoin(posts, eq(users.id, posts.userId));
.leftJoin(...)
.rightJoin(...)
- union/unionAll
const query = db
.select({ name: users.name })
.from(users)
.where(ilike(users.email, "%@test.com%"))
.unionAll(
db.select({ name: users.name }).from(users).where(ilike(users.name, "%@太%")));
- 生のSQL
await db.execute(sql<型もつけれます>`SELECT * FROM users WHERE email = ${'alice@example.com'}`);
- サブクエリ
const sub = db.select({ count: sql<number>`COUNT(*)` }).from(users).as('user_count');
const result = await db.select().from(sub);
まとめ
初めてdrizzleを触ってみましたが、個人的にはとても扱いやすいと感じました。
理由はやはりSQLライクにクエリをかけるため、ORM独自のインプットが少ないことです。
この記事では基本的にクエリ操作についてしか触れていませんが、スキーマ定義やseedの反映など、
かなり直感的に実装でき、tsの型安全も即座に保証が効く点がとても魅力的だと感じました。
今回の記事が誰かのお役に立てれば幸いです。
NCDC株式会社( ncdc.co.jp/ )のテックブログです。 主にエンジニアチームのメンバーが投稿します。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください!
Discussion