Nuxt SQL Database

Setup your SQL database in Nuxt with Drizzle ORM, including installation, environment setup, and database connection.

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.

Getting started

Install dependencies

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
NuxtHub automatically detects your database connection using environment variables:
  • Uses PGlite (embedded PostgreSQL) if no environment variables are set.
  • Uses postgres-js driver if you set DATABASE_URL, POSTGRES_URL, or POSTGRESQL_URL environment variable.
  • Use neon-http driver with @neondatabase/serverless for Neon serverless PostgreSQL.

Set SQL dialect

Enable the database in your nuxt.config.ts by setting the db property to your desired SQL dialect:

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    db: 'postgresql'
  }
})

Database schema

Create your database schema with full TypeScript support using Drizzle ORM:

server/db/schema.ts
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(),
})
Learn more about defining the database schema files.

Generate migrations

Generate the database migrations from your schema:

Terminal
npx nuxt db generate

This creates SQL migration files in server/db/migrations/{dialect}/ which are automatically applied during deployment and development.

That's it! You can now start your development server and query your database using the db instance from hub:db.
Make sure to run npx nuxt db generate to generate the database migrations each time you change your database schema and restart the development server.

Local development

During local development, view and edit your database from Nuxt DevTools using the Drizzle Studio:

At the moment, Drizzle Studio does not support SQLite.

Build-time Hooks

'hub:db:migrations:dirs'
(dirs: string[]) => void | Promise<void>
Add additional directories to scan for database migration files (.sql).
nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'hub:db:migrations:dirs': (dirs) => {
      dirs.push('my-module/db/migrations')
    }
  }
})
'hub:db:queries:paths'
(queries: string[], dialect: string) => void | Promise<void>
Add queries that are not tracked in the _hub_migrations table which are applied after the database migrations complete.
nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'hub:db:queries:paths': (queries, dialect) => {
      queries.push('my-module/db/queries')
    }
  }
})
'hub:db:schema:extend'
({ paths: string[], dialect: string }) => void | Promise<void>
Extend the database schema with additional files.
modules/my-module/index.ts
import { createResolver, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    nuxt.hook('hub:db:schema:extend', ({ paths, dialect }) => {
      paths.push(resolve(`./db/schema/pages.${dialect}.ts`))
    })
  }
})
Learn more about Nuxt server hooks on the Nuxt documentation.

Advanced configuration

For advanced use cases, you can explicitly configure the database connection:

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    db: {
      dialect: 'postgresql',
      driver: 'postgres-js', // Optional: explicitly choose driver
      connection: {
        connectionString: process.env.DATABASE_URL
      }
    }
  }
})

Column casing

Database model names often use snake_case conventions, while in TypeScript, it is common to use camelCase for naming models. To address this, you can use the casing option to automatically map camelCase JavaScript keys to snake_case in the database:

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    db: {
      dialect: 'postgresql',
      casing: 'snake_case'
    }
  }
})

This allows you to use camelCase in your TypeScript schema while Drizzle automatically maps them to snake_case in the database:

server/db/schema.ts
import { pgTable, text, serial, timestamp } from 'drizzle-orm/pg-core'

export const users = pgTable('users', {
  id: serial().primaryKey(),
  firstName: text().notNull(), // Maps to first_name in the database
  lastName: text().notNull(),  // Maps to last_name in the database
  createdAt: timestamp().notNull().defaultNow() // Maps to created_at
})
Learn more about camel and snake casing in Drizzle ORM.

D1 over HTTP

Use the d1-http driver to access a Cloudflare D1 database over HTTP. This is useful when you want to query your D1 database when hosting on other platforms.

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    db: {
      dialect: 'sqlite',
      driver: 'd1-http'
    }
  }
})

This driver requires the following environment variables:

VariableDescription
NUXT_HUB_CLOUDFLARE_ACCOUNT_IDYour Cloudflare account ID
NUXT_HUB_CLOUDFLARE_API_TOKENA Cloudflare API token with D1 permissions
NUXT_HUB_CLOUDFLARE_DATABASE_IDThe ID of your D1 database
You can find your Cloudflare account ID and create API tokens in the Cloudflare dashboard. The API token needs D1:Edit permissions.

Neon Serverless

Use the neon-http driver to connect to Neon serverless PostgreSQL. This driver uses HTTP protocol optimized for serverless environments.

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    db: {
      dialect: 'postgresql',
      driver: 'neon-http'
    }
  }
})

Install the required dependency:

pnpm add @neondatabase/serverless

This driver requires the following environment variable:

VariableDescription
DATABASE_URLYour Neon database connection string (or POSTGRES_URL / POSTGRESQL_URL)
You can find your Neon connection string in the Neon dashboard. The connection string format is postgresql://user:password@hostname/database.

AI Agents

If you work with an IDE that supports AI agents, you can add the following text in your Agents.md or .cursor/rules file:

# Agent Instructions

/** ... your agent instructions ... */

## Database

- **Database Dialect**: The database dialect is set in the `nuxt.config.ts` file, within the `hub.db` option or `hub.db.dialect` property.
- **Drizzle Config**: Don't generate the `drizzle.config.ts` file manually, it is generated automatically by NuxtHub.
- **Generate Migrations**: Use `npx nuxt db generate` to automatically generate database migrations from schema changes
- **Never Write Manual Migrations**: Do not manually create SQL migration files in the `server/db/migrations/` directory
- **Workflow**:
  1. Create or modify the database schema in `server/db/schema.ts` or any other schema file in the `server/db/schema/` directory
  2. Run `npx nuxt db generate` to generate the migration
  3. Run `npx nuxt db migrate` to apply the migration to the database, or run `npx nuxt dev` to apply the migration during development
- **Access the database**: Use the `db` instance from `hub:db` to query the database, it is a Drizzle ORM instance.

Migrating from v0.9

Breaking changes in NuxtHub v0.10: If you're upgrading from a previous version that used hubDatabase(), follow this migration guide.

Configuration changes

The database option has been renamed to db and now accepts a SQL dialect instead of a boolean.

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
-    database: true
+    db: 'sqlite'
  }
})

Valid dialects are sqlite, postgresql and mysql.

Directory changes

The database directory has been renamed from server/database/ to server/db/:

- server/database/schema.ts
+ server/db/schema.ts

- server/database/migrations/
+ server/db/migrations/

Make sure to move your schema and migration files to the new location.

API changes

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 result = await db.select().from(tables.users)

Migration files

Your existing SQL migration files continue to work! Just move them to server/db/migrations/.