NuxtHub Database provides a type-safe SQL database powered by Drizzle ORM, supporting PostgreSQL, MySQL, and SQLite with smart detection and automatic migrations at build time.
Install Drizzle ORM, Drizzle Kit, and the appropriate driver(s) for the database you are using:
pnpm add drizzle-orm drizzle-kit postgres @electric-sql/pglite
yarn add drizzle-orm drizzle-kit postgres @electric-sql/pglite
npm install drizzle-orm drizzle-kit postgres @electric-sql/pglite
bun add drizzle-orm drizzle-kit postgres @electric-sql/pglite
deno add npm:drizzle-orm drizzle-kit postgres @electric-sql/pglite
npx nypm add drizzle-orm drizzle-kit postgres @electric-sql/pglite
PGlite (embedded PostgreSQL) if no environment variables are set.postgres-js driver if you set DATABASE_URL, POSTGRES_URL, or POSTGRESQL_URL environment variable.pnpm add drizzle-orm drizzle-kit mysql2
yarn add drizzle-orm drizzle-kit mysql2
npm install drizzle-orm drizzle-kit mysql2
bun add drizzle-orm drizzle-kit mysql2
deno add npm:drizzle-orm drizzle-kit mysql2
npx nypm add drizzle-orm drizzle-kit mysql2
mysql2 driver if you set DATABASE_URL or MYSQL_URL environment variable.pnpm add drizzle-orm drizzle-kit @libsql/client
yarn add drizzle-orm drizzle-kit @libsql/client
npm install drizzle-orm drizzle-kit @libsql/client
bun add drizzle-orm drizzle-kit @libsql/client
deno add npm:drizzle-orm drizzle-kit @libsql/client
npx nypm add drizzle-orm drizzle-kit @libsql/client
libsql driver for Turso if you set TURSO_DATABASE_URL and TURSO_AUTH_TOKEN environment variables.libsql locally with file at .data/database/sqlite.db if no environment variables are set.Enable the database in your nuxt.config.ts by setting the database property to your desired SQL dialect:
export default defineNuxtConfig({
hub: {
database: 'postgresql'
}
})
export default defineNuxtConfig({
hub: {
database: 'mysql'
}
})
export default defineNuxtConfig({
hub: {
database: 'sqlite'
}
})
Create your database schema with full TypeScript support using Drizzle ORM:
import { pgTable, text, serial, timestamp } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial().primaryKey(),
name: text().notNull(),
email: text().notNull().unique(),
password: text().notNull(),
avatar: text().notNull(),
createdAt: timestamp().notNull().defaultNow(),
})
import { mysqlTable, text, serial, timestamp } from 'drizzle-orm/mysql-core'
export const users = mysqlTable('users', {
id: serial().primaryKey(),
name: text().notNull(),
email: text().notNull().unique(),
password: text().notNull(),
avatar: text().notNull(),
createdAt: timestamp().notNull().defaultNow(),
})
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
export const users = sqliteTable('users', {
id: integer().primaryKey({ autoIncrement: true }),
name: text().notNull(),
email: text().notNull().unique(),
password: text().notNull(),
avatar: text().notNull(),
createdAt: integer({ mode: 'timestamp' }).notNull(),
})
server/database/schema.tsserver/database/schema/*.ts.{dialect}.ts suffix, e.g. server/database/schema.postgresql.ts will be only imported if hub.database is postgresql.The merged schema is exported in hub:database:schema or via the schema object in the hub:database namespace:
import * as schema from 'hub:database:schema'
// or
import { schema } from 'hub:database'
hub:database:schema:extend hook:import { defineNuxtModule, createResolver } from 'nuxt/kit'
export default defineNuxtModule({
setup(options, nuxt) {
const { resolvePath } = createResolver(import.meta.url)
nuxt.hook('hub:database:schema:extend', async ({ dialect, paths }) => {
// Add your module drizzle schema files for the given dialect
// e.g. ./schema/pages.postgresql.ts if hub.database is 'postgresql'
paths.push(await resolvePath(`./schema/pages.${dialect}`))
})
}
})
Generate the database migrations from your schema:
npx nuxthub database generate
This creates SQL migration files in server/database/migrations/{dialect}/ which are automatically applied during deployment and development.
db instance from hub:database.npx nuxthub database generate to generate the database migrations each time you change your database schema and restart the development server.Now that you have your database schema and migrations set up, you can start querying your database.
import { db, schema } from 'hub:database'
export default eventHandler(async (event) => {
return await db.query.users.findMany()
// or
return await db.select().from(schema.users)
})
import { db, schema } from 'hub:database'
export default eventHandler(async (event) => {
const { name, email } = await readBody(event)
return await db
.insert(schema.users)
.values({
name,
email,
createdAt: new Date()
})
.returning()
})
import { db, schema } from 'hub:database'
export default eventHandler(async (event) => {
const { id } = getRouterParams(event)
const { name } = await readBody(event)
return await db
.update(schema.users)
.set({ name })
.where(eq(tables.users.id, Number(id)))
.returning()
})
import { db, schema } from 'hub:database'
export default eventHandler(async (event) => {
const { id } = getRouterParams(event)
const deletedUser = await db
.delete(schema.users)
.where(eq(schema.users.id, Number(id)))
.returning()
if (!deletedUser) {
throw createError({
statusCode: 404,
message: 'User not found'
})
}
return { deleted: true }
})
Database migrations provide version control for your database schema. NuxtHub supports SQL migration files (.sql) and automatically applies them during development and deployment. Making them fully compatible with Drizzle Kit generated migrations.
.<dialect>.sql suffix (e.g., 0001_create-todos.postgresql.sql).NuxtHub scans server/database/migrations for migrations in each Nuxt layer.
To scan additional directories, specify them in your config:
export default defineNuxtConfig({
hub: {
database: {
dialect: 'postgresql',
migrationsDirs: [
'server/database/custom-migrations/'
]
}
}
})
For more control (e.g., in Nuxt modules), use the hub:database:migrations:dirs hook:
import { createResolver, defineNuxtModule } from 'nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'my-auth-module'
},
setup(options, nuxt) {
const { resolve } = createResolver(import.meta.url)
nuxt.hook('hub:database:migrations:dirs', (dirs) => {
dirs.push(resolve('./db-migrations'))
})
}
})
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL
);
.data/database/migrations when you run Nuxt, giving you a consolidated view.Migrations are automatically applied when you:
npx nuxi dev)npx nuxi build)Applied migrations are tracked in the _hub_migrations database table.
Once you have updates your database schema, you can generate new migrations using the following command:
npx nuxthub database generate
This will generate new migrations files in server/database/migrations/{dialect}/ which are automatically applied during development and deployment.
Once you have generated new migrations, you can apply them using the following command:
npx nuxthub database migrate
This will apply the new migrations to your database.
_hub_migrations. Ensure they're idempotent.Use the hub:database:queries:paths hook to run additional queries after migrations:
import { createResolver, defineNuxtModule } from 'nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'my-auth-module'
},
setup(options, nuxt) {
const { resolve } = createResolver(import.meta.url)
nuxt.hook('hub:database:queries:paths', (paths, dialect) => {
paths.push(resolve(`./db-queries/seed-admin.${dialect}.sql`))
})
}
})
INSERT OR IGNORE INTO admin_users (id, email, password) VALUES (1, 'admin@nuxt.com', 'admin');
.data/database/queries when you run Nuxt, giving you a consolidated view.For Cloudflare D1 with Drizzle ORM migrations, replace:
-PRAGMA foreign_keys = OFF;
+PRAGMA defer_foreign_keys = on;
ALTER TABLE ...
-PRAGMA foreign_keys = ON;
+PRAGMA defer_foreign_keys = off;
You can populate your database with initial data using Nitro Tasks:
export default defineNuxtConfig({
nitro: {
experimental: {
tasks: true
}
}
})
export default defineTask({
meta: {
name: 'db:seed',
description: 'Seed database with initial data'
},
async run() {
console.log('Seeding database...')
const users = [
{
name: 'John Doe',
email: 'john@example.com',
password: 'hashed_password',
avatar: 'https://i.pravatar.cc/150?img=1',
createdAt: new Date()
},
{
name: 'Jane Doe',
email: 'jane@example.com',
password: 'hashed_password',
avatar: 'https://i.pravatar.cc/150?img=2',
createdAt: new Date()
}
]
await useDrizzle().insert(tables.users).values(users)
return { result: 'Database seeded successfully' }
}
})
Open the Tasks tab in Nuxt DevTools and click on the db:seed task.
During local development, view and edit your database from Nuxt DevTools using the Drizzle Studio:

For advanced use cases, you can explicitly configure the database connection:
export default defineNuxtConfig({
hub: {
database: {
dialect: 'postgresql',
driver: 'postgres-js', // Optional: explicitly choose driver
connection: {
connectionString: process.env.DATABASE_URL
}
}
}
})
hubDatabase(), follow this migration guide.The database option now accepts a SQL dialect instead of a boolean to enable it.
export default defineNuxtConfig({
hub: {
- database: true
+ database: 'sqlite'
}
})
Valid dialects are sqlite, postgresql and mysql.
The old hubDatabase() function has been removed. You must now use Drizzle ORM.
Before:
const db = hubDatabase()
const result = await db.prepare('SELECT * FROM users').all()
After:
const db = useDrizzle()
const result = await db.select().from(tables.users)
Your existing SQL migration files continue to work! No changes needed.