314 lines
6.2 KiB
Markdown
314 lines
6.2 KiB
Markdown
# Database with Drizzle ORM and db0
|
|
|
|
**Check if installed:** Look for `drizzle-orm` and optionally `db0` in package.json before using.
|
|
|
|
## Overview
|
|
|
|
Drizzle ORM is a TypeScript-first ORM. In Nuxt, it's commonly used with Nitro's `db0` for database access.
|
|
|
|
## Setup
|
|
|
|
### Database Configuration
|
|
|
|
```typescript
|
|
// server/database/index.ts
|
|
import { drizzle } from "drizzle-orm/better-sqlite3"
|
|
import Database from "better-sqlite3"
|
|
|
|
const sqlite = new Database("sqlite.db")
|
|
export const db = drizzle(sqlite)
|
|
```
|
|
|
|
### Schema Definition
|
|
|
|
```typescript
|
|
// server/database/schema.ts
|
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"
|
|
|
|
export const users = sqliteTable("users", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
name: text("name").notNull(),
|
|
email: text("email").notNull().unique(),
|
|
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
|
|
() => new Date(),
|
|
),
|
|
})
|
|
|
|
export const posts = sqliteTable("posts", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
title: text("title").notNull(),
|
|
content: text("content"),
|
|
authorId: integer("author_id").references(() => users.id),
|
|
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
|
|
() => new Date(),
|
|
),
|
|
})
|
|
```
|
|
|
|
## CRUD Operations
|
|
|
|
### Insert
|
|
|
|
```typescript
|
|
// server/api/users.post.ts
|
|
import { db } from "~/server/database"
|
|
import { users } from "~/server/database/schema"
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const body = await readBody(event)
|
|
|
|
const [user] = await db
|
|
.insert(users)
|
|
.values({
|
|
name: body.name,
|
|
email: body.email,
|
|
})
|
|
.returning()
|
|
|
|
return user
|
|
})
|
|
```
|
|
|
|
### Select
|
|
|
|
```typescript
|
|
// server/api/users.get.ts
|
|
import { db } from "~/server/database"
|
|
import { users } from "~/server/database/schema"
|
|
import { eq } from "drizzle-orm"
|
|
|
|
export default defineEventHandler(async () => {
|
|
// Get all users
|
|
const allUsers = await db.select().from(users)
|
|
|
|
// Get user by id
|
|
const user = await db.select().from(users).where(eq(users.id, 1))
|
|
|
|
// Get with specific columns
|
|
const userEmails = await db
|
|
.select({
|
|
email: users.email,
|
|
name: users.name,
|
|
})
|
|
.from(users)
|
|
|
|
return allUsers
|
|
})
|
|
```
|
|
|
|
### Update
|
|
|
|
```typescript
|
|
import { db } from "~/server/database"
|
|
import { users } from "~/server/database/schema"
|
|
import { eq } from "drizzle-orm"
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const id = getRouterParam(event, "id")
|
|
const body = await readBody(event)
|
|
|
|
const [updated] = await db
|
|
.update(users)
|
|
.set({ name: body.name })
|
|
.where(eq(users.id, Number(id)))
|
|
.returning()
|
|
|
|
return updated
|
|
})
|
|
```
|
|
|
|
### Delete
|
|
|
|
```typescript
|
|
import { db } from "~/server/database"
|
|
import { users } from "~/server/database/schema"
|
|
import { eq } from "drizzle-orm"
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const id = getRouterParam(event, "id")
|
|
|
|
await db.delete(users).where(eq(users.id, Number(id)))
|
|
|
|
return { success: true }
|
|
})
|
|
```
|
|
|
|
## Queries
|
|
|
|
### With Joins
|
|
|
|
```typescript
|
|
import { db } from "~/server/database"
|
|
import { users, posts } from "~/server/database/schema"
|
|
import { eq } from "drizzle-orm"
|
|
|
|
export default defineEventHandler(async () => {
|
|
const usersWithPosts = await db
|
|
.select()
|
|
.from(users)
|
|
.leftJoin(posts, eq(users.id, posts.authorId))
|
|
|
|
return usersWithPosts
|
|
})
|
|
```
|
|
|
|
### Relational Queries
|
|
|
|
```typescript
|
|
// Define relations
|
|
import { relations } from "drizzle-orm"
|
|
|
|
export const usersRelations = relations(users, ({ many }) => ({
|
|
posts: many(posts),
|
|
}))
|
|
|
|
export const postsRelations = relations(posts, ({ one }) => ({
|
|
author: one(users, {
|
|
fields: [posts.authorId],
|
|
references: [users.id],
|
|
}),
|
|
}))
|
|
|
|
// Query with relations
|
|
const usersWithPosts = await db.query.users.findMany({
|
|
with: {
|
|
posts: true,
|
|
},
|
|
})
|
|
```
|
|
|
|
### Filtering
|
|
|
|
```typescript
|
|
import { eq, ne, gt, gte, lt, lte, like, and, or } from "drizzle-orm"
|
|
|
|
// Equal
|
|
const user = await db.select().from(users).where(eq(users.id, 1))
|
|
|
|
// Greater than
|
|
const recentPosts = await db
|
|
.select()
|
|
.from(posts)
|
|
.where(gt(posts.createdAt, new Date("2024-01-01")))
|
|
|
|
// Like (pattern matching)
|
|
const searchUsers = await db
|
|
.select()
|
|
.from(users)
|
|
.where(like(users.name, "%john%"))
|
|
|
|
// Multiple conditions
|
|
const filtered = await db
|
|
.select()
|
|
.from(users)
|
|
.where(
|
|
and(eq(users.active, true), gt(users.createdAt, new Date("2024-01-01"))),
|
|
)
|
|
```
|
|
|
|
### Ordering and Limiting
|
|
|
|
```typescript
|
|
import { desc, asc } from "drizzle-orm"
|
|
|
|
const latestPosts = await db
|
|
.select()
|
|
.from(posts)
|
|
.orderBy(desc(posts.createdAt))
|
|
.limit(10)
|
|
|
|
const paginatedUsers = await db
|
|
.select()
|
|
.from(users)
|
|
.orderBy(asc(users.name))
|
|
.limit(20)
|
|
.offset(40)
|
|
```
|
|
|
|
## Transactions
|
|
|
|
```typescript
|
|
export default defineEventHandler(async (event) => {
|
|
const body = await readBody(event)
|
|
|
|
const result = await db.transaction(async (tx) => {
|
|
const [user] = await tx
|
|
.insert(users)
|
|
.values({
|
|
name: body.name,
|
|
email: body.email,
|
|
})
|
|
.returning()
|
|
|
|
const [post] = await tx
|
|
.insert(posts)
|
|
.values({
|
|
title: body.postTitle,
|
|
authorId: user.id,
|
|
})
|
|
.returning()
|
|
|
|
return { user, post }
|
|
})
|
|
|
|
return result
|
|
})
|
|
```
|
|
|
|
## Migrations
|
|
|
|
### Generate Migration
|
|
|
|
```bash
|
|
npx drizzle-kit generate:sqlite
|
|
```
|
|
|
|
### Run Migration
|
|
|
|
```typescript
|
|
// server/database/migrate.ts
|
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator"
|
|
import { db } from "./index"
|
|
|
|
migrate(db, { migrationsFolder: "./drizzle" })
|
|
```
|
|
|
|
## Type Safety
|
|
|
|
Drizzle provides full TypeScript type inference:
|
|
|
|
```typescript
|
|
// Types are inferred automatically
|
|
const users = await db.select().from(users)
|
|
// users: { id: number; name: string; email: string; createdAt: Date }[]
|
|
|
|
// Partial selects are typed too
|
|
const names = await db.select({ name: users.name }).from(users)
|
|
// names: { name: string }[]
|
|
```
|
|
|
|
## Using with db0 (Nitro)
|
|
|
|
```typescript
|
|
// server/database/index.ts
|
|
import { drizzle } from "drizzle-orm/d1"
|
|
|
|
export default defineNitroPlugin(() => {
|
|
// Access db0 database
|
|
const db = drizzle(useDatabase())
|
|
|
|
return {
|
|
provide: {
|
|
db,
|
|
},
|
|
}
|
|
})
|
|
|
|
// Usage in API routes
|
|
export default defineEventHandler(async (event) => {
|
|
const { $db } = event.context
|
|
const users = await $db.select().from(users)
|
|
return users
|
|
})
|
|
```
|