NuxtHub supports defining the database schema in multiple files and directories, allowing you to organize your schema files in a way that makes sense for your project, but also open the possibility to Nuxt modules to extend the database schema.
Database schema can be defined in a single file or in multiple files, these files are scanned and automatically imported following this glob pattern:
server/db/schema.tsserver/db/schema.{dialect}.tsserver/db/schema/*.tsserver/db/schema/*.{dialect}.tsThe merged schema is exported in hub:db:schema or via the schema object in the hub:db namespace:
import * as schema from 'hub:db:schema'
// or
import { schema } from 'hub:db'
.nuxt/hub/db/schema.mjs.Database schema is scanned and automatically imported for each Nuxt layer.
This meands that you can also define schema in the layers directory:
layers/cms/server/db/schema.ts
layers/products/server/db/schema/products.ts
If you are a Nuxt module developer, you can also extend the database schema by using the hub:db:schema:extend hook:
import { defineNuxtModule, createResolver } from '@nuxt/kit'
export default defineNuxtModule({
setup(options, nuxt) {
const { resolvePath } = createResolver(import.meta.url)
nuxt.hook('hub:db:schema:extend', async ({ dialect, paths }) => {
// Add your module drizzle schema files for the given dialect
// e.g. ./schema/pages.postgresql.ts if hub.db is 'postgresql'
paths.push(await resolvePath(`./schema/pages.${dialect}`))
})
}
})
Types inferred from your database schema are only available on the server-side by default. To share these types with your Vue application, you can use the shared/ directory which is auto-imported across both server and client.
Create a types file in the shared/types/ directory:
import { users, posts } from 'hub:db:schema'
// Select types (for reading data)
export type User = typeof users.$inferSelect
export type Post = typeof posts.$inferSelect
// Insert types (for creating data)
export type NewUser = typeof users.$inferInsert
export type NewPost = typeof posts.$inferInsert
These types are now auto-imported and available in your Vue components, composables, and API routes:
<script setup lang="ts">
const { data: users } = await useFetch<User[]>('/api/users')
</script>
import { db, schema } from 'hub:db'
export default eventHandler(async (event) => {
const body = await readBody<NewUser>(event)
return await db.insert(schema.users).values(body).returning()
})
Pick and Omit TypeScript's built-in utility types.// User without password for public API responses
export type PublicUser = Omit<User, 'password'>
// Only the fields needed for user creation form
export type UserForm = Pick<NewUser, 'name' | 'email' | 'password'>
You can populate your database with initial data using Nitro Tasks:
export default defineNuxtConfig({
nitro: {
experimental: {
tasks: true
}
}
})
import { db, schema } from 'hub:db'
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 db.insert(schema.users).values(users)
return { result: 'Database seeded successfully' }
}
})
Open the Tasks tab in Nuxt DevTools and click on the db:seed task.