Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/content/docs/2.features/0.database.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,38 @@ export default defineNuxtConfig({
})
```

### 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:

```ts [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:

```ts [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
})
```

::callout{to="https://orm.drizzle.team/docs/sql-schema-declaration#camel-and-snake-casing" external}
Learn more about [camel and snake casing](https://orm.drizzle.team/docs/sql-schema-declaration#camel-and-snake-casing) in Drizzle ORM.
::

### Cloudflare 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.
Expand Down
8 changes: 4 additions & 4 deletions src/db/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ResolvedDatabaseConfig } from '@nuxthub/core'
* Creates a Drizzle client for the given configuration
*/
export async function createDrizzleClient(config: ResolvedDatabaseConfig, hubDir: string) {
const { driver, connection } = config
const { driver, connection, casing } = config
let client

let pkg = ''
Expand All @@ -18,14 +18,14 @@ export async function createDrizzleClient(config: ResolvedDatabaseConfig, hubDir
})
pkg = 'drizzle-orm/postgres-js'
const { drizzle } = await import(pkg)
return drizzle({ client })
return drizzle({ client, casing })
} else if (driver === 'neon-http') {
const clientPkg = '@neondatabase/serverless'
const { neon } = await import(clientPkg)
const sql = neon(connection.url)
pkg = 'drizzle-orm/neon-http'
const { drizzle } = await import(pkg)
return drizzle(sql)
return drizzle(sql, { casing })
} else if (driver === 'libsql') {
pkg = 'drizzle-orm/libsql'
} else if (driver === 'mysql2') {
Expand All @@ -38,5 +38,5 @@ export async function createDrizzleClient(config: ResolvedDatabaseConfig, hubDir
}

const { drizzle } = await import(pkg)
return drizzle({ connection })
return drizzle({ connection, casing })
}
22 changes: 12 additions & 10 deletions src/db/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ async function generateDatabaseSchema(nuxt: Nuxt, hub: ResolvedHubConfig) {
}

async function setupDatabaseClient(nuxt: Nuxt, hub: ResolvedHubConfig) {
const { dialect, driver, connection, mode } = hub.db as ResolvedDatabaseConfig
const { dialect, driver, connection, mode, casing } = hub.db as ResolvedDatabaseConfig

// For types, d1-http uses sqlite-proxy
const driverForTypes = driver === 'd1-http' ? 'sqlite-proxy' : driver
Expand Down Expand Up @@ -237,10 +237,11 @@ declare module 'hub:db' {

// Generate simplified drizzle() implementation
const modeOption = dialect === 'mysql' ? `, mode: '${mode || 'default'}'` : ''
const casingOption = casing ? `, casing: '${casing}'` : ''
let drizzleOrmContent = `import { drizzle } from 'drizzle-orm/${driver}'
import * as schema from './db/schema.mjs'

const db = drizzle({ connection: ${JSON.stringify(connection)}, schema${modeOption} })
const db = drizzle({ connection: ${JSON.stringify(connection)}, schema${modeOption}${casingOption} })
export { db, schema }
`

Expand All @@ -251,7 +252,7 @@ import { PGlite } from '@electric-sql/pglite'
import * as schema from './db/schema.mjs'

const client = new PGlite(${JSON.stringify(connection.dataDir)})
const db = drizzle({ client, schema })
const db = drizzle({ client, schema${casingOption} })
export { db, schema, client }
`

Expand All @@ -270,7 +271,7 @@ import * as schema from './db/schema.mjs'
const client = postgres('${connection.url}', {
onnotice: () => {}
})
const db = drizzle({ client, schema });
const db = drizzle({ client, schema${casingOption} });
export { db, schema }
`
}
Expand All @@ -280,7 +281,7 @@ import { drizzle } from 'drizzle-orm/neon-http'
import * as schema from './db/schema.mjs'

const sql = neon(${connection.connectionString})
const db = drizzle(sql, { schema })
const db = drizzle(sql, { schema${casingOption} })
export { db, schema }
`
}
Expand All @@ -294,7 +295,7 @@ function getDb() {
if (!_db) {
const binding = process.env.DB || globalThis.__env__?.DB || globalThis.DB
if (!binding) throw new Error('DB binding not found')
_db = drizzle(binding, { schema })
_db = drizzle(binding, { schema${casingOption} })
}
return _db
}
Expand Down Expand Up @@ -353,7 +354,7 @@ async function d1HttpDriver(sql, params, method) {
return { rows }
}

const db = drizzle(d1HttpDriver, { schema })
const db = drizzle(d1HttpDriver, { schema${casingOption} })

export { db, schema }
`
Expand All @@ -369,7 +370,7 @@ function getDb() {
if (!_db) {
const hyperdrive = process.env.${bindingName} || globalThis.__env__?.${bindingName} || globalThis.${bindingName}
if (!hyperdrive) throw new Error('${bindingName} binding not found')
_db = drizzle({ connection: hyperdrive.connectionString, schema${modeOption} })
_db = drizzle({ connection: hyperdrive.connectionString, schema${modeOption}${casingOption} })
}
return _db
}
Expand All @@ -390,14 +391,15 @@ export { db, schema }

async function setupDatabaseConfig(nuxt: Nuxt, hub: ResolvedHubConfig) {
// generate drizzle.config.ts in .nuxt/hub/db/drizzle.config.ts
const { dialect } = hub.db as ResolvedDatabaseConfig
const { dialect, casing } = hub.db as ResolvedDatabaseConfig
const casingConfig = casing ? `\n casing: '${casing}',` : ''
addTemplate({
filename: 'hub/db/drizzle.config.ts',
write: true,
getContents: () => `import { defineConfig } from 'drizzle-kit'

export default defineConfig({
dialect: '${dialect}',
dialect: '${dialect}',${casingConfig}
schema: '${relative(nuxt.options.rootDir, resolve(nuxt.options.buildDir, 'hub/db/schema.mjs'))}',
out: '${relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, `server/db/migrations/${dialect}`))}'
});` })
Expand Down
8 changes: 8 additions & 0 deletions src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ export type DatabaseConfig = {
* @see https://orm.drizzle.team/docs/rqb#modes
*/
mode?: 'default' | 'planetscale'
/**
* Database model naming convention for Drizzle ORM.
* When set to `'snake_case'`, automatically maps camelCase JavaScript keys to snake_case database column names.
*
* @see https://orm.drizzle.team/docs/sql-schema-declaration#camel-and-snake-casing
*/
casing?: 'snake_case' | 'camelCase'
}

export type ResolvedDatabaseConfig = DatabaseConfig & {
Expand All @@ -196,4 +203,5 @@ export type ResolvedDatabaseConfig = DatabaseConfig & {
migrationsDirs: string[]
queriesPaths: string[]
applyMigrationsDuringBuild: boolean
casing?: 'snake_case' | 'camelCase'
}
Loading