Coding Gun

วิธีการใช้งาน Drizzle ORM

Drizzle คือ ORM ที่ทำหน้าที่ติดต่อกับ Database ซึ่งจะคล้ายกับ Prisma แต่มีขนาดเล็กมาๆ Drizzle มีขนาดแค่ 7.4Kb หลังจาก Minified และ Zip ด้วย Gzip แล้ว

Drizzle นั้นรองรับ Database ดังๆหลายตัว ยกตัวอย่างเช่น

ในบทความนี้เราจะใช้ SQLite เป็น Database และใช้ better-sqlite3 เป็น Library สำหรับการเชื่อมต่อกับ SQLite ซึ่งในการทำงานจริงเราอาจเลือกใช้ Library อื่นๆ เช่น libsql ในการเชื่อมต่อก็ได้

ติดตั้ง Drizzle

เรามาเริ่มต้นติดตั้ง Drizzle กัน โดยที่คุณจะต้องมี Drizzle และ Better SQlite3

npm install drizzle-orm better-sqlite3

หลังจากนั้นให้คุณติดตั้ง Drizzle Kit ซึ่งเป็น GUI สำหรับการ Manage Database เหมือนกับ Prisma Studio

npm install -D drizzle-kit

เราใช้ -D หรือ --save-dev เพื่อบอกว่า Drizzle Kit นั้นใช้แค่ตอน Dev เท่านั้นตอนขึ้น Production ไม่ต้องเอา Drizzle Kit ขึ้นไปด้วย

Config Drizzle

สร้างไฟล์ drizzle.config.ts และบอก Drizzle Kit ว่า schema และ migration อยู่ที่ไหน

// drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./src/db/schema.ts", // ไฟล์ Schema ที่ระบุโครงสร้างของแต่ละ Table ใน Database
  out: "./drizzle",        // SQL ที่ generate ออกมาจะเก็บไว้ใน Folder ไหน
  dialect: "sqlite",       
  driver: "better-sqlite", // ระบุ Driver ที่ใช้ในการติดต่อกับ Database
  dbCredentials: {
    url: "./sqlite.db",    // Database จะถูกสร้างโดยอัตโนมัติถ้ายังไม่มีไฟล์นี้
  },
});

กำหนด Schema

หลังจากกำหนด Configuration ของ Drizzle เรียบร้อยแล้ว เราก็จะกำหนดโครงสร้างของ Table ต่างๆใน Database ซึ่งเราจะสร้างไฟล์ schema.ts ไว้ใน Folder /src/db

// src/db/schema.ts
import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core";
import { relations } from "drizzle-orm";

export const users = sqliteTable("users", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  name: text("name").notNull(),
  email: text("email").notNull().unique(),
});

export const posts = sqliteTable("posts", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  title: text("title").notNull(),
  body: text("body"),
  authorId: integer("author_id")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
});

ในกรณีที่ต้องการสร้างความสัมพันธ์(Relationship) เราจะกำหนดความสัมพันธ์ของแต่ละ Table ดังนี้

// User มีความสัมพันธ์แบบ One-To-Many กับ Post
export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
}));

Connect Database

การ Connect Database เราจะใช้การ new Database() ขึ้นมาแล้วระบุ Database URL

// src/db/index.ts
import Database from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";
import * as schema from "./schema";

const sqlite = new Database("sqlite.db"); // จะสร้างไฟล์ sqlite.db ขึ้นมาใหม่ถ้ายังไม่มี
export const db = drizzle(sqlite, { schema });

Generate และ Migrate database

เพิ่ม script สำหรับการ Migrate และ Generate Database เข้าไปใน package.json ซึ่ง

{
  "scripts": {
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate",
    "db:studio": "drizzle-kit studio"
  }
}

หลังจากนั้นเราจะสามารถเรียกคำสั่ง migrate และ generate ผ่านทาง npm run แบบนี้ได้เลย

npm run db:generate
npm run db:migrate
npm run db:studio   # เปิด UI จัดการ DB

Seeding

Seeding คือขั้นตอนการนำข้อมูลเข้าไปใน Database เนื่องจาก Application จะทำงานได้นอกจากจะมีโครงสร้างของ Database ที่ถูกต้องแล้วยังต้องมี Master Data หรือข้อมูลเริ่มต้นด้วย เช่น Order Status เป็นต้น

ดังนั้นถ้าเราต้องการนำข้อมูลเข้าไปใส่ใน Database หลังจากที่ Migrate เรียบร้อยแล้วให้เราสร้าง Seeding โดยมีขั้นตอนต่างๆ ดังนี้

1. สร้าง Seed Script

สร้างไฟล์ src/db/seed.ts แล้วหลังจากนั้นลบข้อมูลเก่าออกก่อน แล้วเพิ่มข้อมูลใหม่ใส่เข้าไป ไม่งั้นเมื่อ Run Seed หลายๆรอบข้อมูลจะเพิ่มขึ้นเรื่อยๆ

// src/db/seed.ts
import { db } from "./index";
import { users, posts } from "./schema";

async function main() {
  console.log("Seeding database...");

  // ล้างข้อมูลเก่า (ถ้าต้องการเริ่มใหม่ทุกครั้ง)
  await db.delete(posts);
  await db.delete(users);

  // เพิ่ม users
  const insertedUsers = await db.insert(users).values([
    { name: "Alice", email: "alice@test.com" },
    { name: "Bob", email: "bob@test.com" },
    { name: "Charlie", email: "charlie@test.com" },
  ]).returning();

  // เพิ่ม posts
  await db.insert(posts).values([
    {
      title: "Hello World",
      body: "My first post using Drizzle + SQLite!",
      authorId: insertedUsers[0].id,
    },
    {
      title: "SQLite is fast",
      body: "Better-sqlite3 is great for local dev.",
      authorId: insertedUsers[1].id,
    },
    {
      title: "Relational Queries",
      body: "Drizzle makes joins super easy.",
      authorId: insertedUsers[2].id,
    },
  ]);

  console.log("Seeding completed!");
}

main()
  .then(() => process.exit(0))
  .catch((err) => {
    console.error(err);
    process.exit(1);
  });

2. เพิ่ม Script ใน package.json

{
  "scripts": {
    "db:seed": "ts-node src/db/seed.ts"
  }
}

ถ้าคุณใช้ tsx ก็แค่เปลี่ยนจาก ts-node เป็น tsx ได้เลย

{
  "scripts": {
    "db:seed": "tsx src/db/seed.ts"
  }

3. รัน Seed Script

สุดท้ายเราก็ไปเลือก Seed Script ที่เราเขียนมาใช้งานโดยใช้คำสั่ง

npm run db:seed

ผลลัพธ์ที่ได้จะออกมาเป็น

ตัวอย่างการใช้ Drizzle

ในตัวอย่างนี้เป็นการเรียกใช้ Drizzle ใน Application ของเราโดยในตัวอย่างนี้เราจะ Insert User ใหม่เข้าไปใน Table Users

import { db } from "./db";
import { users, posts } from "./db/schema";
import { eq } from "drizzle-orm";

// Create
await db.insert(users).values({ name: "John", email: "john@test.com" });
await db.insert(posts).values({ title: "Hello", body: "SQLite + Drizzle", authorId: 4 });

// Get all User
const allUsers = await db.select().from(users);
const userWithPosts = await db.query.users.findMany({
  with: { posts: true },
});

// Update user
await db.update(users)
  .set({ name: "Jack" })
  .where(eq(users.id, 4));

// Delete post
await db.delete(posts)
  .where(eq(posts.id, 1));

สรุปข้อดีของ Drizzle ORM

เราสามารถสรุปข้อดีของของ Drizzle ORM ออกมาเป็นข้อๆได้ดังนี้

Phanupong Permpimol
Follow me