Drizzle ORM
Setup
To enhance your Developer Experience with the database, we can create a useDrizzle() server composable with few steps.
Install Drizzle
- Install the
drizzle-ormpackage to your project:
pnpm add drizzle-orm
- Install
drizzle-kitdevelopment dependency to your project:
pnpm add -D drizzle-kit
drizzle.config.ts
Add a drizzle.config.ts file to your project:
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'sqlite',
schema: './server/database/schema.ts',
out: './server/database/migrations'
})
Database Schema
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(),
password: text('password').notNull(),
avatar: text('avatar').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
})
npm run db:generate
Let's add a db:generate script to the package.json:
{
"scripts": {
"db:generate": "drizzle-kit generate"
}
}
When running the npm run db:generate command, drizzle-kit will generate the migrations based on server/database/schema.ts and save them in the server/database/migrations directory.
Migrations
Migrations created with npm run db:generate are automatically applied during deployment, preview and when starting the development server.
useDrizzle()
Lastly, we can create a useDrizzle() server composable to interact with the database:
import { drizzle } from 'drizzle-orm/d1'
export { sql, eq, and, or } from 'drizzle-orm'
import * as schema from '../database/schema'
export const tables = schema
export function useDrizzle() {
return drizzle(hubDatabase(), { schema })
}
export type User = typeof schema.users.$inferSelect
We are exporting the tables object and the useDrizzle function to be used in our API handlers without having to import them (Nuxt does it for us as long as it's exported from a server/utils/ file).
This allows you to conveniently reference your tables and interact directly with the Drizzle API.
User type, which is inferred from the users table. This is useful for type-checking the results of your queries.sql, eq, and, and or functions from drizzle-orm to be used in our queries.Seed the database (Optional)
You can add a server task to populate your database with initial data. This uses Nitro Tasks, which is currently an experimental feature.
- Update your nuxt.config.js:
export default defineNuxtConfig({
nitro: {
experimental: {
tasks: true
}
}
})
- Create a new file containing the task:
export default defineTask({
meta: {
name: 'db:seed',
description: 'Run database seed task'
},
async run() {
console.log('Running DB seed task...')
const users = [
{
name: 'John Doe',
email: 'john@example.com',
password: 'password123',
avatar: 'https://example.com/avatar/john.png',
createdAt: new Date()
},
{
name: 'Jane Doe',
email: 'jane@example.com',
password: 'password123',
avatar: 'https://example.com/avatar/jane.png',
createdAt: new Date()
}
]
await useDrizzle().insert(tables.users).values(users)
return { result: 'success' }
}
})
To run the seed task, start your dev server and open the Nuxt DevTools. Go to Tasks and you will see the db:seed task ready to run. This will add the seed data to your database and give you the first users to work with.
Usage
Select
export default eventHandler(async () => {
const todos = await useDrizzle().select().from(tables.todos).all()
return todos
})
Insert
export default eventHandler(async (event) => {
const { title } = await readBody(event)
const todo = await useDrizzle().insert(tables.todos).values({
title,
createdAt: new Date()
}).returning().get()
return todo
})
Update
export default eventHandler(async (event) => {
const { id } = getRouterParams(event)
const { completed } = await readBody(event)
const todo = await useDrizzle().update(tables.todos).set({
completed
}).where(eq(tables.todos.id, Number(id))).returning().get()
return todo
})
Delete
export default eventHandler(async (event) => {
const { id } = getRouterParams(event)
const deletedTodo = await useDrizzle().delete(tables.todos).where(and(
eq(tables.todos.id, Number(id))
)).returning().get()
if (!deletedTodo) {
throw createError({
statusCode: 404,
message: 'Todo not found'
})
}
return deletedTodo
})