Skip to content
Open
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
33 changes: 31 additions & 2 deletions docs/content/docs/1.getting-started/3.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,11 +506,12 @@ Experimental features that are not yet stable.

### `experimental.sqliteConnector`

SQLite connectors have limitations in different environments. Some work in serverless environments, while others do not. Nuxt Content supports three different SQLite connectors to cover all environments:
SQLite connectors have limitations in different environments. Some work in serverless environments, while others do not. Nuxt Content supports four different SQLite connectors to cover all environments:

- `better-sqlite3`: Works in all Node environments, GitHub CI, Vercel CI and production, Cloudflare CI pipelines, etc. (Does **not** work in WebContainers and StackBlitz)
- `better-sqlite3`: Works in all Node environments, GitHub CI, Vercel CI and production, Cloudflare CI pipelines, etc. (Does **not** work in WebContainers, StackBlitz, or Bun runtime)
- `sqlite3`: Works in Node environments, GitHub CI, and StackBlitz. (Does **not** work in Vercel or Cloudflare)
- `native`: As of Node.js v22.5.0, the `node:sqlite` module is available natively in Node.js. This connector works in all Node environments with Node.js version 22.5.0 or newer.
- `bun`: Uses Bun's built-in `bun:sqlite` module. Required when deploying to Bun runtime (e.g., Vercel with `bun1.x` runtime). Automatically selected when building with Bun, but must be explicitly set when building with Node.js for a Bun runtime target.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
Expand All @@ -520,6 +521,34 @@ export default defineNuxtConfig({
});
```

When deploying to Vercel with Bun runtime, the connector is auto-detected if you configure the runtime via Nitro:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
nitro: {
vercel: {
functions: {
runtime: 'bun1.x',
},
},
},
});
```

Alternatively, you can explicitly set the connector:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
content: {
experimental: { sqliteConnector: 'bun' },
},
});
```

::prose-note
When `sqliteConnector: 'bun'` is set but the build runs on Node.js (common in CI/CD pipelines like Vercel), the build-time content processing will automatically fall back to a Node.js-compatible connector (e.g., `better-sqlite3`). Only the deployed server runtime will use `bun:sqlite`. No additional configuration is needed.
::

### `experimental.nativeSqlite` (deprecated, use `sqliteConnector`)

As of Node.js v22.5.0, the `node:sqlite` module is available natively in Node.js.
Expand Down
12 changes: 9 additions & 3 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,14 @@ export default defineNuxtModule<ModuleOptions>({
const preset = findPreset(nuxt)
await preset.setupNitro(config, { manifest, resolver, moduleOptions: options, nuxt })

const resolveOptions = { resolver, sqliteConnector: options.experimental?.sqliteConnector || (options.experimental?.nativeSqlite ? 'native' : undefined) }
const sqliteConnector = options.experimental?.sqliteConnector || (options.experimental?.nativeSqlite ? 'native' : undefined)
const resolveOptions = { resolver, sqliteConnector }
config.alias ||= {}
config.alias['#content/adapter'] = await resolveDatabaseAdapter(config.runtimeConfig!.content!.database?.type || options.database.type, resolveOptions)
config.alias['#content/local-adapter'] = await resolveDatabaseAdapter(options._localDatabase!.type || 'sqlite', resolveOptions)
// Build-time local adapter must use a connector compatible with the build environment.
// When targeting Bun but building on Node.js, fall back to a Node.js-compatible connector.
const localSqliteConnector = (sqliteConnector === 'bun' && !process.versions.bun) ? undefined : sqliteConnector
config.alias['#content/local-adapter'] = await resolveDatabaseAdapter(options._localDatabase!.type || 'sqlite', { resolver, sqliteConnector: localSqliteConnector })

config.handlers ||= []
manifest.collections.forEach((collection) => {
Expand Down Expand Up @@ -281,8 +285,10 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
const collectionDump: Record<string, string[]> = {}
const collectionChecksum: Record<string, string> = {}
const collectionChecksumStructure: Record<string, string> = {}
const requestedConnector = options.experimental?.sqliteConnector || (options.experimental?.nativeSqlite ? 'native' : undefined)
const buildConnector = (requestedConnector === 'bun' && !process.versions.bun) ? undefined : requestedConnector
const db = await getLocalDatabase(options._localDatabase, {
sqliteConnector: options.experimental?.sqliteConnector || (options.experimental?.nativeSqlite ? 'native' : undefined),
sqliteConnector: buildConnector,
})
const databaseContents = await db.fetchDevelopmentCache()

Expand Down
8 changes: 7 additions & 1 deletion src/presets/vercel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import nodePreset from './node'
export default definePreset({
name: 'vercel',
parent: nodePreset,
async setup(options) {
async setup(options, nuxt) {
options.database ||= { type: 'sqlite', filename: '/tmp/contents.sqlite' }

const vercelRuntime = (nuxt.options.nitro as Record<string, unknown>)?.vercel as { functions?: { runtime?: string } } | undefined
if (typeof vercelRuntime?.functions?.runtime === 'string' && vercelRuntime.functions.runtime.startsWith('bun')) {
options.experimental ||= {}
options.experimental.sqliteConnector ||= 'bun'
}
},
async setupNitro(nitroConfig) {
const database = nitroConfig.runtimeConfig?.content?.database
Expand Down
3 changes: 1 addition & 2 deletions src/runtime/internal/database.server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { H3Event } from 'h3'
import { isAbsolute } from 'pathe'
import type { Connector } from 'db0'
import type { ConnectorOptions as SqliteConnectorOptions } from 'db0/connectors/better-sqlite3'
import { decompressSQLDump } from './dump'
import { fetchDatabase } from './api'
import { refineContentFields } from './collection'
Expand Down Expand Up @@ -210,7 +209,7 @@ function refineDatabaseConfig(config: RuntimeConfig['content']['database']) {
}

if (config.type === 'sqlite') {
const _config = { ...config } as SqliteConnectorOptions
const _config = { ...config } as { path?: string, name?: string }
if (config.filename === ':memory:') {
return { name: ':memory:' }
}
Expand Down
7 changes: 6 additions & 1 deletion src/types/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export interface ModuleOptions {
/**
* Use given SQLite connector instead of `better-sqlite3` if available
*
* - `better-sqlite3`: Default for Node.js environments
* - `native`: Use Node.js built-in `node:sqlite` (requires Node.js >= 22.5.0)
* - `sqlite3`: Use `sqlite3` package (works in StackBlitz/WebContainers)
* - `bun`: Use Bun's built-in `bun:sqlite` (for Bun runtime targets, e.g. Vercel with bun1.x)
*
* @default undefined
*/
sqliteConnector?: SQLiteConnector
Expand All @@ -232,4 +237,4 @@ export interface PublicRuntimeConfig {
}
}

export type SQLiteConnector = 'native' | 'sqlite3' | 'better-sqlite3'
export type SQLiteConnector = 'native' | 'sqlite3' | 'better-sqlite3' | 'bun'
15 changes: 9 additions & 6 deletions src/utils/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ export async function resolveDatabaseAdapter(adapter: 'sqlite' | 'bunsqlite' | '
}

adapter = adapter || 'sqlite'
if (adapter === 'sqlite' && process.versions.bun) {
return databaseConnectors.bunsqlite
}

if (adapter === 'sqlite') {
return await findBestSqliteAdapter({ sqliteConnector: opts.sqliteConnector, resolver: opts.resolver })
}
Expand Down Expand Up @@ -168,11 +164,13 @@ export async function getLocalDatabase(database: SqliteDatabaseConfig | D1Databa
}

async function findBestSqliteAdapter(opts: { sqliteConnector?: SQLiteConnector, resolver?: Resolver }) {
if (process.versions.bun) {
if (opts.sqliteConnector === 'bun') {
if (!process.versions.bun) {
console.warn('[nuxt/content] `sqliteConnector: \'bun\'` targets Bun runtime β€” the build-time database will use a Node.js-compatible fallback.')
}
return opts.resolver ? opts.resolver.resolve('./runtime/internal/connectors/bun-sqlite') : 'db0/connectors/bun-sqlite'
}

// if node:sqlite is available, use it
if (opts.sqliteConnector === 'native' && isNodeSqliteAvailable()) {
return opts.resolver ? opts.resolver.resolve('./runtime/internal/connectors/node-sqlite') : 'db0/connectors/node-sqlite'
}
Expand All @@ -187,6 +185,11 @@ async function findBestSqliteAdapter(opts: { sqliteConnector?: SQLiteConnector,
return 'db0/connectors/better-sqlite3'
}

// Auto-detect Bun runtime when no explicit connector is set
if (process.versions.bun) {
return opts.resolver ? opts.resolver.resolve('./runtime/internal/connectors/bun-sqlite') : 'db0/connectors/bun-sqlite'
}

if (isWebContainer()) {
await ensurePackageInstalled('sqlite3')

Expand Down
Loading